今天在网上看到了1987年国际C语言混乱代码大赛获奖的一行代码,代码只有一行,却穷尽了C中的精华。下面对这一行代码进行分析:

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

任何复杂的东西源自自己了解的知识太少了,知道必须的一些东西后再复杂的东西也会变得简单。我查找了一个解释,觉得讲的不错,就直接引用了,原文的作者是一个大佬,我很佩服,计算机的前辈。

int main() 
{
    /* unix被编译器内定为一个宏 
     * 相当于#define unix 1     */

    printf("unix=%d\n", unix); /* =1 */ 

    /* 打印字符串"un",因为"fun"是个字符数组
     * "fun"+1相当于字符指针右移,指向"un" */
    printf("%s\n","fun"+1);

    /* "have"是个字符数组,"have"[1]即字符a
     * 输出97,即第二个字符'a'的ASCII值。*/
    printf("%d\n", "have"[1]);
    printf("%d\n", 'a');

    /* 在C语言中,x[1] = 1[x] */
    printf("%d\n", (1)["have"]);

    /* 97 - 96 = 0x61 - 0x60 = 1 */
    printf("%d\n", (1)["have"] - 0x60);

    /* 所以 "fun"+((1)["have"]-0x60) 相当于"fun"+1,输出"un" */
    printf("%s\n", "fun" + ((1)["have"] - 0x60));

    /* 将其中的1用unix代替 */
    printf("%s\n", (unix)["have"]+"fun"-0x60);

    /* 以上为后半部分 = "un" */

    /* 下面两个都输出"bcde", 因为指针都是从'b'开始 */
    printf("%s\n", "abcde" + 1);
    printf("%s\n", &"abcde"[1]);

    /* &"abcde"[1] == &(1)["abcde"]  输出一样 */
    printf("%s\n", &(1)["abcde"]);

    /* 1用unix代替 */
    printf("%s\n", &unix["abcde"]);

    /* 下面输出"%six" 并换行 */
    printf("%s", &"?%six\n"[1]);

    /* 注意:
       \012 = 0x0a = \n, 
       第一个字符 \021 被跳过
       \0 是空字符  */

    /* 同样输出"%six" 并换行 */
    printf("%s", &"\021%six\012\0"[1]);

    /* 相当于这样 */
    printf("%s", &unix["\021%six\012\0"]);

    /* 把字符串"%six\n"当作格式,输出"ABix" */
    printf(&unix["\021%six\012\0"], "AB");

    /* 相当于这样 */
    printf("%six\n", "AB");

    /* 所以下面的可以输出"unix" */
    printf("%six\n", (unix)["have"]+"fun"-0x60);

    /* 至此,问题解决!!!输出"unix" */
    printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);

    return 0;
}

如果坚持看到了这里,那一行的代码也就变得简单易懂了,写这行代码的作者不愧是贝尔实验室的大佬。