这个C程序是如何工作的
main({u448&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&
它按原样编译(在gcc 4.6.3上测试)。它在编译时打印时间。在我的系统上:
!!!!!!
!! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !!
!! !!!!!! !! !! !! !! !! !!!!!!
!! !! !! !! !! !! !!
!! !! !! !! !! !! !!
!! !!!!!! !! !! !! !!!!!!
来源:sykes2-一行中的时钟,sykes2作者提示
一些提示:默认情况下没有编译警告。使用-Wall编译时,会发出以下警告:
sykes2.c:1:1:警告:返回类型默认为“int”[-Wreturn-type]
sykes2.c:在函数“main”中:
sykes2.c:1:14:警告:未使用计算值[-Wunused value]
sykes2.c:1:1:警告:函数“putchar”的隐式声明[-Wimplicit函数声明]
sykes2.c:1:1:警告:建议在“|”[-wparenthess]的操作数中的算术值周围加括号
sykes2.c:1:1:警告:建议在“|”[-wparenthess]的操作数中的算术值周围加括号
sykes2.c:1:1:警告:控件到达非无效函数的末尾[-Wreturn类型]
让我们把它弄清楚
缩进:
main(\uux){
_^448&;amp;amp;amp;amp;main(-);
putchar(--\u64
?32 |-~7【|时间【|-】/8%8】/(| 2?1:8)%8&1
: 10);
}
引入变量来解决这一混乱局面:
main(内部i){
如果(i^448)
主(-~i);
如果(--i%64){
字符a=-~7[\uuuuuu时间/8%8][“>;'txiZ^(~z?”-48];
字符b=a>;>;“;;==~$::199”[i*2&;8&;i/64]/(i&;2?1:8)%8;
普查尔(32 |(b&;1));
}否则{
putchar(10);//换行符
}
}
请注意,-~i==i+1,因为有两个补码
main(内部i){
如果(i!=448)
主管道(i+1);
我--;
如果(i%64==0){
putchar('\n');
}否则{
字符a=-~7[\uuuuuu时间/8%8][“>;'txiZ^(~z?”-48];
字符b=a>;>;“;;==~$::199”[i*2&;8&;i/64]/(i&;2?1:8)%8;
普查尔(32 |(b&;1));
}
}
现在,请注意a[b]与b[a]相同,并再次应用-~==1+更改:
main(内部i){
如果(i!=448)
主管道(i+1);
我--;
如果(i%64==0){
putchar('\n');
}否则{
字符a=(“>;'txiZ^(~z?”-48)[(时间-i/8%8)[7]+1;
字符b=a>;>;“;;==~$::199”[(i*2&;8)| i/64]/(i&;2?1:8)%8;
普查尔(32 |(b&;1));
}
}
将递归转换为循环,并进一步简化:
//请不要传递任何命令行参数
main(){
int i;
对于(i=447;i>;=0;i--){
如果(i%64==0){
putchar('\n');
}否则{
字符t=uuuu时间[7-i/8%8];
字符a=“>;'txiZ^(~z?”[t-48]+1;
int shift=“;;;===~$::199”[(i*2和8)|(i/64)];
如果((i&;2)==0)
移位/=8;
班次=班次%8;
字符b=a>;>;移位;
普查尔(32 |(b&;1));
}
}
}
这将在每次迭代中输出一个字符。每64个字符输出一个换行符。否则,它将使用一对数据表来确定要输出的内容,并放置字符32(空格)或字符33(a!)。第一个表(“>;'txiZ^(~z?”)是一组描述每个字符外观的10个位图,第二个表(“;;;;;====~$::199“)从位图中选择要显示的适当位
第二张桌子
让我们从检查第二个表开始,int shift=“;;;;===~$::199”[(i*2&;8)|(i/64)]i/64是行号(6到0),i*2&;8是8,如果i是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)。移位表的结果如下:
行列值
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
注意,作者对前两个表条目使用了null终止符(鬼鬼祟祟的!)
这是在七段显示之后设计的,其中7s为空白。因此,第一个表中的条目必须定义点亮的段
第一桌
\uuuu TIME\uuuu是预处理器定义的特殊宏。它扩展为字符串常量,包含预处理器运行的时间,格式为“HH:MM:SS”。请注意,它正好包含8个字符。请注意,0-9的ASCII值为48到57,而:的ASCII值为58。输出为每行64个字符,因此\uuuuuuuuuu时间\uuuuuuuuu的每个字符只剩下8个字符
因此,7-i/8%8是当前正在输出的\uuuuuuuuu时间的索引(需要7-,因为我们向下迭代i)。因此,t是正在输出的\uuuuuuuuuu时间的特征
a最终等于以下二进制值,具体取决于输入t:
0 00111111
1 00101000
2 01110101
3 01111001
4 01101010
5 01011011
6 01011111
7 00101001
8 01111111
9 01111011
: 01000000
每个数字都是一个位图,描述在我们的七段显示中点亮的段。由于字符都是7位ASCII,高位总是被清除。因此,段表中的7始终打印为空白。第二个表如下图所示,其中7s为空白:
000055
11 55
11 55
116655
22 33
22 33
444433
因此,例如,4是0110100(设置了位1、3、5和6),它打印为
-------
!!--!!--
!!--!!--
!!!!!!--
----!!--
----!!--
----!!--
为了显示我们真正理解了代码,让我们使用下表稍微调整一下输出:
00
11 55
11 55
66
22 33
22 33
44
这被编码为“?;;?==?”::799\x07“。出于艺术目的,我们将在一些字符中添加64位(因为只使用低6位,这不会影响输出);这将给出“?{{}gg::799G”(请注意,第8个字符未使用,因此我们可以实际制作任何我们想要的字符).将新表放入原始代码中:
main({u448&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&;&
我们得到
!!
!! !! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !!
正如我们所预料的,它不像原作那样坚固,这也解释了作者为什么选择使用他制作的表格