Obfuscated C Code Contest 2006. Please explain sykes2.c
这个C程序是如何工作的?
1 | main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1:10);} |
它按原样编译(在
1 2 3 4 5 6 7 | !! !!!!!! !! !!!!!! !! !!!!!! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !!!!!! !! !! !! !! !! !!!!!! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !!!!!! !! !! !! !!!!!! |
来源:Sykes2-一行一个时钟,Sykes2作者提示
一些提示:默认情况下没有编译警告。用
1 2 3 4 5 6 7 | 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] |
让我们把它弄模糊。
Indenting:
1 2 3 4 5 6 | main(_) { _^448 && main(-~_); putchar(--_%64 ? 32 | -~7[__TIME__-_/8%8][">'txiZ^(~z?"-48] >>";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1 : 10); } |
引入变量来解决这一混乱局面:
1 2 3 4 5 6 7 8 9 10 11 |
注意,
1 2 3 4 5 6 7 8 9 10 11 12 13 |
现在,请注意,
1 2 3 4 5 6 7 8 9 10 11 12 13 |
将递归转换为循环,并进一步简化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // please don't pass any command-line arguments main() { int i; for(i=447; i>=0; i--) { if(i % 64 == 0) { putchar(' '); } 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(
让我们从检查第二张表开始,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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 |
或以表格形式
1 2 3 4 5 6 7 | 00005577 11775577 11775577 11665577 22773377 22773377 44443377 |
注意,作者在前两个表条目中使用了空终止符(鬼鬼祟祟!).
这是在七段显示之后设计的,其中
因此,
根据输入
1 2 3 4 5 6 7 8 9 10 11 | 0 00111111 1 00101000 2 01110101 3 01111001 4 01101010 5 01011011 6 01011111 7 00101001 8 01111111 9 01111011 : 01000000 |
每个数字都是一个位图,描述在七段显示中亮起的段。因为字符都是7位ASCII,所以高位总是被清除。因此,段表中的
1 2 3 4 5 6 7 | 000055 11 55 11 55 116655 22 33 22 33 444433 |
例如,
1 2 3 4 5 6 7 | ----!!-- !!--!!-- !!--!!-- !!!!!!-- ----!!-- ----!!-- ----!!-- |
为了证明我们真正理解代码,让我们用下表稍微调整一下输出:
1 2 3 4 5 6 7 | 00 11 55 11 55 66 22 33 22 33 44 |
编码为
1 | main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>"?{{?}}?gg::799G"[_*2&8|_/64]/(_&2?1:8)%8&1:10);} |
我们得到
1 2 3 4 5 6 7 | !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! |
如我们所料。它不像原版那样结实,这就解释了作者为什么选择使用他做的表格。
让我们将其格式化以便于阅读:
1 2 3 4 | main(_){ _^448&&main(-~_); putchar((--_%64) ? (32|-(~7[__TIME__-_/8%8])[">'txiZ^(~z?"-48]>>(";;;====~$::199")[_*2&8|_/64]/(_&2?1:8)%8&1):10); } |
因此,在没有参数的情况下运行它时(argc传统上)是
这样,它将打印7 64个字符宽的行(外部三元条件和
1 2 3 4 5 6 7 |
现在,
1 2 | -(~(7[__TIME__-argc/8%8][">'txiZ^(~z?"-48]) >> (";;;====~$::199"[argc*2&8|argc/64]) / (argc&2?1:8) % 8 |
正如另一个海报所说,
1 | 7[__TIME__ - (argc/8)%8] |
这将选择
除其他方案外,
我尽可能地消除模运算的模糊性,并删除了重写。
1 2 3 4 5 6 7 8 9 10 11 |
再扩展一点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | int pixelX, line, digit, shift; char shiftChar; for(line=6; line >= 0; line--){ for (digit =0; digit<8; digit++){ for(pixelX=7;pixelX >= 0; pixelX--){ shiftChar =";;;====~$::199"[pixelX*2 & 8 | line]; if (pixelX & 2) shift = shiftChar & 7; else shift = shiftChar >> 3; putchar(' '| (">'txiZ^(~z?"["12:34:56"[digit]-'0'] + 1) >> shift & 1 ); } } putchar(' '); } |