作者:Shiky Chang
链接:https://www.zhihu.com/question/37692782/answer/74409370
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
!!! 多图流量预警 !!!
Update Log
––––––––––––––––––––––––––––––––––––––––––––––––––––––
12/3/15
- 重新用电脑排了版,之前手机写的,发现版面好烂
- 修正求圆周率程序(K&R vs ANSI C)
- 更新大量真·阿卡林军团 & 炮姐程序
––––––––––––––––––––––––––––––––––––––––––––––––––––––
如果说「瞠目结舌」的话,IOCCC 上随便拿一篇获奖代码出来就足以让人下巴落地了。
The International Obfuscated C Code Contest
一个比较经典的例子是 1988 年得奖的代码,这个程序直接估算字符面积求圆周率,可读性算是比较友好的:
•westley.c•
#define _ F-->00||-F-OO--;
int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO()
{
_-_-_-_
_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_
_-_-_-_
}
注:这段程序实际上是 1989 年修正过的,由于 88 年原来程序代码没有考虑到 ANSI C 的编译标准,导致在处理例如
#define _ -i
-_
的时候,老旧的 K&R 框架和 ANSI C 结果不一样:K&R 是直接的
--i
而 ANSI C 编译结果实际上等同于
-(-i)
因此之前的程序现在运行的话出来的结果是 0.250,而不是 3.141。修正过的程序就没有这个问题了。
又比如 13 年有个只有一行的程序,可以判断从 Franklin Pierce 往后的 31 位美国总统是民主党还是共和党,这个就有点不知所云了:
•cable1.c•
main(int riguing,char**acters){puts(1[acters-~!(*(int*)1[acters]G96'5%riguing)]);}
食用方法:
make cable1
./cable1 obama republican democrat
./cable1 bush republican democrat
总统名要小写,republican 和 democrat 顺序不能颠倒。
经 @chua zier 提醒,历史上的确有重名的美国总统,除了 Johnson 之外,还有 Theodore Roosevelt / Franklin D. Roosevelt,程序原作者注明用
./cable1 roosevelt republican democrat
表示 Theodore Roosevelt,而用
./cable1 fdr republican democrat
表示 Franklin D. Roosevelt。
这一行代码做了这么多事:首先查询输入的总统的名字,然后在一个 look-up table 里面找出对应的政治阵营,再输出出来。问题在于这 31 位总统名字存放在哪里?而这个 look-up table 又存放在哪里?
有趣的是,IOCCC 的评委还提到,你甚至可以用这个程序检测一些 IT 大佬的 Mac / PC 阵营:
./cable1 Cooper Mac PC
./cable1 Noll Mac PC
./cable1 Broukhis Mac PC
./cable1 Jobs Mac PC
./cable1 Gates Mac PC
./cable1 Ballmer Mac PC
难道这个程序暴露了 Ballmer 离开微软的真相?
最近几届比赛的代码为了增加混乱程度,代码越来越长,可读性也越来越差(不过话说回来,让可读性变得越来越差其实原本就是这个比赛的第一宗旨吧),不少代码甚至本身就是个 ASCII artwork……比如 11 年有一只阿卡林:
•akari.c•
为了保持美观我就直接上图了。源代码见此:http://www.ioccc.org/2011/akari/akari.c
–––––––––– !!! 前方阿卡林军团高能预警 !!! ––––––––––
这个阿卡林程序实际上是一个图像 down-sampler,可以接受符合条件的 PGM / PPM 灰度图像或者 LF 换行(不支持 CR-LF)的 ASCII art 为输入,然后转换输出一个处理后的图像 / ASCII art。不过这个阿卡林最逆天的地方在于,它可以用自身的源代码文本作为输入,输出生成另一个可以编译运行的程序的代码!而且把这个生成的程序文本继续作为输入做进一步 down-sample,又可以生成一段可以编译的代码,如此反复,可以套多达4层!详细的食用方法如下:
make akari
./akari akari.c akari2.c
然后生成的阿卡林·2号是这个样子的:
•akari2.c•
看不清?请摘下眼镜或者退远了看。注意,阿卡林·2号也是可以编译运行的,她的功能是把输入的 ACSII 文本的每个字符中间插入空格以及每行之间插入空行,生成一段“疏松”了的文本。我们用阿卡林·2号自己做实验品:
make akari2.c
./akari2 <akari2.c> akari2fat.txt
成功了!生成了一只阿卡林·2号·舒松:
•akari2fat.txt•
阿卡林·2号还能干别的,她支持一个 rot13 参数:
./akari2 rot13 <akari2.c> akari2fat.txt
生成的是经过 ROT13 仿射变换的文本,我们称之为阿卡林·2号·舒松·加蜜吧!
但是还没完……如果我们把原版阿卡林放进去再来一层呢?
./akari < akari.c | ./akari > akari3.c
于是阿卡林·3号诞生:
•akari3.c•
wm_aoi(n)
/*ity,,[2*/{}char*y=
(")M{lpduKtjsa(v""YY"
"*yuruyuri") ;main(/*
/",U/ R)U* Y0U= ="/\
*/){puts (y 17/*
"NR{I=" ){/=*
=* */);/*
**/{ ;;}}
可怜的阿卡林·3号,由于“马赛克”(down-sample)次数太多,摘了眼镜也只能模糊看到一点点……我们来问问阿卡林·3号对于诞生的感受吧:
make akari3
./akari3
于是她回答:
yuruyuri
居然会说ゆるゆり!
最后,我们尝试生产一下阿卡林·4号:
./akari < akari.c | ./akari | ./akari > akari4.c
•akari4.c•
main
(){puts("Y"
"U RU YU "\
"RI" )/*
*/ ;}
顺利生产!虽然内容已经直截了当了,不过我们还是采访一下她吧:
make akari4
./akari4
她的答复是:
YU RU YU RI
至此,阿卡林军团全部诞生!