这个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 - A clock in one line,sykes2 author hints
一些提示:默认情况下没有编译警告 . 使用 -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]
4 回答
让我们去混淆它 .
缩进:
引入变量来解开这个烂摊子:
注意
-~i == i+1
因为二进制补码 . 因此,我们有现在,请注意a[b] is the same as b[a],然后再次应用
-~ == 1+
更改:将递归转换为循环并稍微简化一下:
每次迭代输出一个字符 . 每64个字符,它输出一个换行符 . 否则,它使用一对数据表来确定要输出的内容,并放置字符32(空格)或字符33(
!
) . 第一个表(">'txiZ^(~z?"
)是一组描述每个字符外观的10个位图,第二个表(";;;====~$::199"
)选择要从位图显示的相应位 .第二张表
让我们从检查第二个表
int shift = ";;;====~$::199"[(i*2&8) | (i/64)];
开始 .i/64
是行号(6到0),i*2&8
是8 iffi
是4,5,6或7 mod 8 .if((i & 2) == 0) shift /= 8; shift = shift % 8
选择表值的高八进制数字(对于i%8
= 0,1,4,5)或低八进制数字(对于i%8
= 2,3,6,7) . 转换表最终看起来像这样:或以表格形式
请注意,作者使用null终止符作为前两个表条目(偷偷摸摸!) .
这是在七段显示后设计的,
7
为空白 . 因此,第一个表中的条目必须定义亮起的段 .第一张表
TIME是预处理器定义的特殊宏 . 它扩展为包含预处理器运行时间的字符串常量,格式为
"HH:MM:SS"
. 注意它包含正好8个字符 . 请注意,0-9的ASCII值为48到57,:
的ASCII值为58.每行输出64个字符,因此每个字符的__TIME__
留下8个字符 .7 - i/8%8
因此是当前正在输出的__TIME__
的索引(需要7-
因为我们向下迭代i
) . 所以,t
是输出__TIME__
的字符 .a
以二进制结束等于以下内容,具体取决于输入t
:每个数字都是一个位图,用于描述在七段显示中亮起的段 . 由于字符都是7位ASCII,因此始终清除高位 . 因此,段表中的
7
始终打印为空白 . 第二个表看起来像这样,7
s为空白:因此,例如,
4
是01101010
(位1,3,5和6集),其打印为为了表明我们真正理解代码,让我们用这个表调整输出:
这被编码为
"?;;?==? '::799\x07"
. 出于艺术目的,我们会影响输出);这给了"?{{?}}?gg::799G"
(注意第8个字符未使用,所以我们实际上可以做任何我们想要的) . 将我们的新表放在原始代码中:我们得到
就像我们预期的那样 . 它不像原版一样坚固,这就解释了为什么作者选择使用他所做的表格 .
我们将其格式化以便于阅读:
因此,在没有参数的情况下运行它,_(传统的argc)是
1
.main()
将递归调用自身,传递-(~_)
的结果(_
的负按位NOT),所以它真的会去448递归(只有条件_^448 == 0
) .考虑到这一点,它将打印7个64字符宽的行(外部三元条件和
448/64 == 7
) . 所以让我们把它改写一点清洁:现在,
32
是ASCII空间的十进制数 . 它打印一个空格或'!'(33是'!',因此'&1
' at the end). Let' s专注于中间的blob:正如另一张海报所说,
__TIME__
是程序的编译时间,并且是一个字符串,因此有一些字符串算法正在进行,并且利用数组下标是双向的:a [b]与b [a]相同]用于字符数组 .这将选择
__TIME__
中前8个字符之一 . 然后将其索引到[">'txiZ^(~z?"-48]
(0-9个字符为十进制48-57) . 必须为其ASCII值选择此字符串中的字符 . 这个相同的字符ASCII代码操作继续通过表达式,导致打印' '或'!',具体取决于字符字形内的位置 .添加到其他解决方案,
-~x
等于x+1
,因为~x
等同于(0xffffffff-x)
. 这相当于2s补码中的(-1-x)
,所以-~x
是-(-1-x) = x+1
.我尽可能地模糊了模数算术并删除了递归
进一步扩展它: