问题

这个C程式如何运作?

main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}

它会编译(在 gcc 4.6.3 上测试).它打印编译时的时间.在我的系统上:

    !!  !!!!!!              !!  !!!!!!              !!  !!!!!! 
    !!  !!  !!              !!      !!              !!  !!  !! 
    !!  !!  !!              !!      !!              !!  !!  !! 
    !!  !!!!!!    !!        !!      !!    !!        !!  !!!!!! 
    !!      !!              !!      !!              !!  !!  !! 
    !!      !!              !!      !!              !!  !!  !! 
    !!  !!!!!!              !!      !!              !!  !!!!!!

资料来源: sykes2 - 单行时钟, sykes2作者提示

一些提示:默认情况下没有编译警告.编译为 -Wall ,会发出以下警告:

sykes2.c:1:1: warning: return type defaults to ‘int’ [-Wreturn-type]
sykes2.c: In function ‘main’:
sykes2.c:1:14: warning: value computed is not used [-Wunused-value]
sykes2.c:1:1: warning: implicit declaration of function ‘putchar’ [-Wimplicit-function-declaration]
sykes2.c:1:1: warning: suggest parentheses around arithmetic in operand of ‘|’ [-Wparentheses]
sykes2.c:1:1: warning: suggest parentheses around arithmetic in operand of ‘|’ [-Wparentheses]
sykes2.c:1:1: warning: control reaches end of non-void function [-Wreturn-type]


解决方法

让我们对它进行模糊处理.

缩排:

main(_) {
    _^448 && main(-~_);
    putchar(--_%64
        ? 32 | -~7[__TIME__-_/8%8][">'txiZ^(~z?"-48] >> ";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1
        : 10);
}

介绍变量以解开这个混乱:

main(int i) {
    if(i^448)
        main(-~i);
    if(--i % 64) {
        char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
        char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
        putchar(32 | (b & 1));
    } else {
        putchar(10); // newline
    }
}

请注意,由于二进制补码, - 〜i == i + 1 .因此,我们有

main(int i) {
    if(i != 448)
        main(i+1);
    i--;
    if(i % 64 == 0) {
        putchar('\n');
    } else {
        char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
        char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
        putchar(32 | (b & 1));
    }
}

现在,请注意, a [b] b [a] < / code> ,然后再次应用 - == == 1 + 更改:

main(int i) {
    if(i != 448)
        main(i+1);
    i--;
    if(i % 64 == 0) {
        putchar('\n');
    } else {
        char a = (">'txiZ^(~z?"-48)[(__TIME__-i/8%8)[7]] + 1;
        char b = a >> ";;;====~$::199"[(i*2&8)|i/64]/(i&2?1:8)%8;
        putchar(32 | (b & 1));
    }
}

将递归转换为循环并稍微简化一下:

// please don't pass any command-line arguments
main() {
    int i;
    for(i=447; i>=0; i--) {
        if(i % 64 == 0) {
            putchar('\n');
        } else {
            char t = __TIME__[7 - i/8%8];
            char a = ">'txiZ^(~z?"[t - 48] + 1;
            int shift = ";;;====~$::199"[(i*2&8) | (i/64)];
            if((i & 2) == 0)
                shift /= 8;
            shift = shift % 8;
            char b = a >> shift;
            putchar(32 | (b & 1));
        }
    }
}

每次迭代输出一个字符.每64个字符,它输出一个换行符.否则,它使用一对数据表来确定输出什么,并且放置字符32(空格)或字符33(一个!).第一个表("> txiZ ^(〜z?")是描述每个字符的外观的10个位图的集合,第二个表(";;; == ==〜$ :: 199")选择从位图中显示的适当位.

The second table

让我们先检查第二个表, int shift =";;; ====〜$ :: 199"[(i * 2&amp; 8)| (i / 64)]; . i / 64 是行号(6至0),并且 i * 2&amp; 8 是8 iff i 是4,5,6或7 mod 8.

if((i&amp; 2)== 0)shift / = 8; shift = shift%8 选择高八进制数字(对于 i%8 = 0,1,4,5)或低八进制数字(对于 i% / code> = 2,3,6,7).移位表最终如下所示:

row col val
6   6-7 0
6   4-5 0
6   2-3 5
6   0-1 7
5   6-7 1
5   4-5 7
5   2-3 5
5   0-1 7
4   6-7 1
4   4-5 7
4   2-3 5
4   0-1 7
3   6-7 1
3   4-5 6
3   2-3 5
3   0-1 7
2   6-7 2
2   4-5 7
2   2-3 3
2   0-1 7
1   6-7 2
1   4-5 7
1   2-3 3
1   0-1 7
0   6-7 4
0   4-5 4
0   2-3 3
0   0-1 7

或以表格形式

00005577
11775577
11775577
11665577
22773377
22773377
44443377

请注意,作者使用空终止符作为前两个表条目(sneaky!).

这是在七段显示器之后设计的,其中 7 为空白.因此,第一个表中的条目必须定义被点亮的段.

The first table

__ TIME __ 是由预处理器.它以"HH:MM:SS"的形式扩展为包含预处理器运行时间的字符串常量.观察它正好包含8个字符.注意,0-9的ASCII值为48到57,而:的ASCII值为58.输出每行64个字符,因此每个 __ TIME __ 的字符留下8个字符.

7 - i / 8%8 因此是当前正在输出的 __ TIME __ 的索引(需要 7 - 是向下迭代 i ).因此, t 是输出的 __ TIME __ 的字符.

根据 t 的输入,

a 最终等于以下二进制数:

0 00111111
1 00101000
2 01110101
3 01111001
4 01101010
5 01011011
6 01011111
7 00101001
8 01111111
9 01111011
: 01000000

每个数字都是一个位图,用于描述在七段显示中亮起的片段.由于字符都是7位ASCII,高位总是被清除.因此,段表中的 7 始终打印为空白.第二个表看起来像这样, 7 为空格:

000055  
11  55  
11  55  
116655  
22  33  
22  33  
444433  

因此,例如, 4 01101010 (位1,3,5和6设置),打印为

----!!--
!!--!!--
!!--!!--
!!!!!!--
----!!--
----!!--
----!!--

为了让我们真正了解代码,让我们用这个表格来调整输出:

  00  
11  55
11  55
  66  
22  33
22  33
  44

This is encoded as "?;;?==? '::799\x07". For artistic purposes, we'll add 64 to a few of the characters (since only the low 6 bits are used, this won't affect the output); this gives "?{{?}}?gg::799G" (note that the 8th character is unused, so we can actually make it whatever we want). Putting our new table in the original code:

main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>"?{{?}}?gg::799G"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}

我们得到

          !!              !!                              !!   
    !!  !!              !!  !!  !!  !!              !!  !!  !! 
    !!  !!              !!  !!  !!  !!              !!  !!  !! 
          !!      !!              !!      !!                   
    !!  !!  !!          !!  !!      !!              !!  !!  !! 
    !!  !!  !!          !!  !!      !!              !!  !!  !! 
          !!              !!                              !!   

正如我们的预期.它不像原始的那样坚固,这就解释了为什么作者选择使用他做的表.




相关问题推荐