为了方便阅读,让我们设置此格式:
此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]
让我们对其进行模糊处理。
缩进:
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]
is the same as b[a]
,然后再次应用a[b]
更改:
b[a]
将递归转换为循环并进行进一步简化:
-~ == 1+
每次迭代输出一个字符。每第64个字符,它将输出一个换行符。否则,它将使用一对数据表找出要输出的内容,并放置字符32(空格)或字符33(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));
}
}
}
)是一组10个位图,描述每个字符的外观,第二个表(!
)从位图中选择要显示的适当位。
让我们开始检查第二张表">'txiZ^(~z?"
。 ";;;====~$::199"
是行号(6到0),如果int shift = ";;;====~$::199"[(i*2&8) | (i/64)];
是4、5、6或7 mod 8,则i/64
是8。
i*2&8
选择表值的高八进制数字(对于i
= 0,1,4,5)或低八进制数字(对于if((i & 2) == 0) shift /= 8; shift = shift % 8
= 2,3,6,7)。移位表最终看起来像这样:
i%8
或以表格形式
i%8
请注意,作者对前两个表条目使用了空终止符(偷偷摸摸!)。
这是在七段显示器之后设计的,其中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
是预处理器定义的特殊宏。它扩展为一个字符串常量,其中包含预处理器的运行时间,格式为7
。请注意,它恰好包含8个字符。请注意,0-9的ASCII值从48到57,__TIME__
的ASCII值58。输出每行64个字符,因此__TIME__
的每个字符保留8个字符。
"HH:MM:SS"
因此是当前正在输出的:
的索引(需要__TIME__
,因为我们向下迭代7 - i/8%8
)。因此,__TIME__
是正在输出的7-
的字符。
i
最终等于二进制的以下内容,具体取决于输入t
:
__TIME__
每个数字是一个位图
,描述在我们的七段显示器中点亮的段。由于所有字符均为7位ASCII,因此始终清除高位。因此,段表中的a
始终打印为空白。第二张表看起来像这样,其中t
为空白:
0 00111111 1 00101000 2 01110101 3 01111001 4 01101010 5 01011011 6 01011111 7 00101001 8 01111111 9 01111011 : 01000000
因此,例如,
7
是7
(设置的位1、3、5和6),其打印为000055 11 55 11 55 116655 22 33 22 33 444433
为了显示我们对代码的理解,让我们对该表进行一些调整:
4
此编码为
01101010
。出于艺术目的,我们会将64个字符添加到一些字符中(因为仅使用了低6位,所以这不会影响输出);这给出----!!-- !!--!!-- !!--!!-- !!!!!!-- ----!!-- ----!!-- ----!!--
(请注意,第8个字符尚未使用,因此我们实际上可以根据需要进行设置)。将我们的新表放在原始代码中:00 11 55 11 55 66 22 33 22 33 44
我们得到
"?;;?==? '::799\x07"
正如我们所期望的。它不像原始的一样美观,这解释了为什么作者选择使用他所做的表格。
为了方便阅读,让我们设置此格式:
"?{{?}}?gg::799G"
因此,不带任何参数运行它,_(通常为argc)为main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>"?{{?}}?gg::799G"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}
。 !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !!
将递归调用自身,传递main(_){
_^448&&main(-~_);
putchar((--_%64) ? (32|-(~7[__TIME__-_/8%8])[">'txiZ^(~z?"-48]>>(";;;====~$::199")[_*2&8|_/64]/(_&2?1:8)%8&1):10);
}
的结果(1
的按位负非),因此实际上它将进行448次递归(仅在main()
的情况下)。
[因此,它将打印7个64个字符的宽行(外部三元条件和-(~_)
)。因此,让我们重写它一点点:
_
现在,_^448 == 0
对于ASCII空间为十进制。它会打印一个空格或一个'!' (33是'!',因此最后是'448/64 == 7
')。让我们关注中间的blob:
main(int argc) { if (argc^448) main(-(~argc)); if (argc % 64) { putchar((32|-(~7[__TIME__-argc/8%8])[">'txiZ^(~z?"-48]>>(";;;====~$::199")[argc*2&8|argc/64]/(argc&2?1:8)%8&1)); } else putchar('\n'); }
[正如另一位发帖人所说,
32
是程序的编译时间,并且是一个字符串,因此正在进行一些字符串算法,并且利用了数组下标是双向的:a [b]是相同的如b [a]用于字符数组。
&1
这将选择
-(~(7[__TIME__-argc/8%8][">'txiZ^(~z?"-48]) >> (";;;====~$::199"[argc*2&8|argc/64]) / (argc&2?1:8) % 8
中的前8个字符之一。然后将其索引为__TIME__
(0-9个字符为48-57个十进制)。必须为该字符串中的字符选择其ASCII值。相同的字符ASCII代码操作将贯穿表达式继续进行,从而导致打印出''或'!'。取决于角色字形中的位置。
[除其他解决方案外,7[__TIME__ - (argc/8)%8]
等于__TIME__
,因为[">'txiZ^(~z?"-48]
等于-~x
。这等于2s补码中的x+1
,因此~x
为(0xffffffff-x)
。
我尽我所能对模算术进行了模糊处理,并消除了递归
(-1-x)
进一步扩展:
-~x
为了方便阅读,让我们设置此格式:
[除其他解决方案外,7[__TIME__ - (argc/8)%8]
等于__TIME__
,因为[">'txiZ^(~z?"-48]
等于-~x
。这等于2s补码中的x+1
,因此~x
为(0xffffffff-x)
。
我尽我所能对模算术进行了模糊处理,并消除了递归
(-1-x)