小议国际C语言混乱代码大赛——附87年一行的代码分析

时间:2022-03-06 09:47:23

一年前的这个季节看的时候,网上还是停留着06年的信息,还以为就此结束了,今天突然在CSDN首页看到居然更新两期了,围观下大牛们的杰作。

虽然在现实中,我们不可能写出那样的代码,其主要的原因是不方便阅读,而且难以维护,但一行代码中可以学习的东西可能比我们翻半天书的还多,比如下的一行代码就能学到平时很难学到的东西:

printf(&unix["\021%siz\012\0"], (unix)["have"] + "fun" - 0x60);

这行代码是1987年 贝尔实验室的 David Korn 提交了这个获奖作品。

当然我们平时写作是用不到这么高深的东西,但经过如下分解后,这些知识点我们都是否能掌握呢?

如果以上代码在windows平台是不能直接运行的,可以再定义一下:

#define unix 1


在C语言中,数组的引用除了我们常用的 array[num],还可以是num[array],两者是相同。 

所以(unix)["have"] 等于"have"[unix],前面unix定义为1,这里的结果就是have中下标为1的那个字符a。

针对这样的式子:"a" + "fun" - 0x60,再作一次变换,0x61-0x60+"fun",加减交换,小学就学过的,不用解释了吧,十六进制0x61 = 十进制97,正好是小写字母a的ASCII码。

上面式子自然变成了0x01+"fun",一个字符串地址加上一位后会是什么样的呢?这样还不明白的话,把fun当成指针里存的数据来是不是容易理解了呢,如*prt++后的结果“un”,这样就更容易明白了吧。

\012 是ASCII码里的换页.功能与\n相同。

再前面一个%s,打印字符串不作解释。

上面已经说到 array[num]等价num[array]

&unix["\021%siz\012\0"], 再简化一下,&1["\021%siz\n\0"],这里需要把\021处理成一个字符,ASCII码里021是什么也不重要了,我们只把这个转义字符处理成一个就行了,所以这个串理解为:

"\021%siz\012\0" [1]等于"\021%siz\n\0" [1]等于"%siz\n\0"

整个语句就化解为:printf("%siz\n\0","un");其中\0是一个(空),已经不重要了,分析到这里,应该明白这个语句输出的是什么了吧!

You Got It:uniz

分析了这么多,现在明白了,其实原理也是那么简单,当我们第一眼没看出来结果的时候,我们是不是应该觉得有必要复习一下了呢?