一年前的这个季节看的时候,网上还是停留着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
分析了这么多,现在明白了,其实原理也是那么简单,当我们第一眼没看出来结果的时候,我们是不是应该觉得有必要复习一下了呢?