最新帖子 精华区 社区服务 统计排行 搜索 帮助 银行
  • 128阅读
  • 0回复
小枫 离线

级别: 管理员

  • UID1
  • 精华 0
  • 发帖37
  • 财富30 点
  • 威望113 点
  • 贡献值0 点
  • 银元0 块
  • 好评度0 点
  • 在线时间5(时)
  • 注册时间2010-05-29
  • 最后登录2010-08-19
倒序阅读   只看楼主      楼主  发表于: 05-30
更多操作

[分享] C语言混乱代码大赛作品分析

复制代码
  1. #include <stdio.h>
  2. main(t,_,a)char *a;{return!0<t?t<3?
  3. main(-79,-13,a+main(-87,1-_,main(-86,0,a+1)+a)):1,t<_?main(t+1,_,a):3,main(-94,-27+t,a)&&t==2?_<13?
  4. main(2,_+1,"%s %d %d\n"):9:16:t<0?t<-72?
  5. main(_,t,"@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n{n+,/+#n+,/#;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l q#'+d'K#!/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# ){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw' iwk{KK{nl]!/w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c ;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# }'+}##(!!/")
  6. :t<-50?_==*a?putchar(31[a]):main(-65,_,a+1):main((*a=='/')+t,_,a+1)
  7. :0<t?main(2,2,"%s"):*a=='/'||main(0,main(-61,*a,
  8. "!ek;dc [email=i@bK]i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m[/email] .vpbks,fxntdCeghiry"),a+1);}
ujiZM  
怎么样?乍一看蛮吓人的吧?我们今天就分析它啦!可能有人要说,分析这种程序根本没有意义,那我冒昧借用Linus的一句名言“Just for fun”!只当是没事了自我娱乐而已吧!:-) KUF$h Er  
如果你有一个支持语法高亮的编辑器,立刻可以看到,程序中有一大段字符串,我们知道,被双引号括起来的字符串里的内容是不会解释成代码语句的(转义字符就算了吧),那我们第一步就是把这些字符串提取出来,现在代码看起来是这样的: Q! Kn|mnN  
复制代码
  1. #include <stdio.h>
  2. main(t,_,a)
  3. char *a;
  4. {
  5. char * STRA="%s %d %d\n";
  6. char * STRB="@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n{n+,/+#n+,/#;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l q#'+d'K#!/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# ){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw' iwk{KK{nl]!/w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c ;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# }'+}##(!!/";
  7. char * STRC="%s";
  8. char * STRD="!ek;dc [email=i@bK]i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m[/email] .vpbks,fxntdCeghiry";
  9. return
  10. !0<t?t<3?main(-79,-13,a+main(-87,1-_,main(-86,0,a+1)+a))
  11. :1,t<_?main(t+1,_,a):3,main(-94,-27+t,a)&&t==2?_<13?
  12. main(2,_+1,STRA):9:16:t<0?t<-72?main(_,t,STRB)
  13. :t<-50?_==*a?putchar(31[a]):main(-65,_,a+1):main((*a=='/')+t,_,a+1)
  14. :0<t?main(2,2,STRC):*a=='/'||main(0,main(-61,*a,STRD),a+1);
  15. }
L{Q4=p,A  
怎么样?代码是不是一下子清晰了很多?好,我们继续观察:在抽取了字符串后,我们发现程序的实际语句只有一个return(如果觉得不可思议你可以搜索一下";" C语言一个分号对应一条语句嘛,可以发现,除了字符串中的内容,的确只有一个分号)。然后我们又发现,语句里有很多"?"和":",这是什么?对了,是三目运算符,而C语言中三目运算符的优先级基本上是最低的(除了赋值和逗号运算符之外,再次搜索代码部分,发现根本没有赋值语句,而逗号运算符只有两个),我们把'?'':'','当作分隔符,任意两个分隔符直接的内容都用大写字母代替,那么程序可以变成这样: |3 ;u"&(P  
复制代码
  1. #include <stdio.h>
  2. #define A !0<t
  3. #define B t<3
  4. #define C main(-79,-13,a+main(-87,1-_,main(-86,0,a+1)+a))
  5. #define D1 1
  6. #define D2 t<_
  7. #define E main(t+1,_,a)
  8. #define F1 3
  9. #define F2 main(-94,-27+t,a)&&t==2
  10. #define G _<13
  11. #define H main(2,_+1,STRA)
  12. #define I 9
  13. #define J 16
  14. #define K t<0
  15. #define L t<-72
  16. #define M main(_,t,STRB)
  17. #define N t<-50
  18. #define O _==*a
  19. #define P putchar(31[a])
  20. #define Q main(-65,_,a+1)
  21. #define R main((*a=='/')+t,_,a+1)
  22. #define S 0<t
  23. #define T main(2,2,STRC)
  24. #define U *a=='/'||main(0,main(-61,*a,STRD),a+1)
  25. main(t,_,a)
  26. char *a;
  27. {
  28. char * STRA="%s %d %d\n";
  29. char * STRB="@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n{n+,/+#n+,/#;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l q#'+d'K#!/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# ){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw' iwk{KK{nl]!/w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c ;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# }'+}##(!!/";
  30. char * STRC="%s";
  31. char * STRD="!ek;dc [email=i@bK]i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m[/email] .vpbks,fxntdCeghiry";
  32. return
  33. A ? B ? C : D1 , D2 ? E : F1 , F2 ? G ? H : I : J : K ? L ? M : N ? O ? P : Q : R : S ? T : U ;
  34. }
$p jf#P8U  
替换的到底正确与否呢?编译,通过,运行,和原来相同!说明替换成功! -xw 98  
下面的重点就是分析A ? B ? C : D1 , D2 ? E : F1 , F2 ? G ? H : I : J : K ? L ? M : N ? O ? P : Q : R : S ? T : U ;这个语句。 r@olC7&  
我们需要复习一下运算符的优先级和结合性的知识: *e%Dg{_  
不同运算符之间按优先级识别,相同优先级的运算符直接按结合性识别,而"?:"运算符的结合性是从右向左的,那么我们可以模拟编译器读入此语句的方式得到: I<c@uXXV;!  
A "|F. 'qZrm  
A ? ]}p2Tp;1  
A ? B %I_&Ehu  
A ? (B ? ~l(G6/R  
A ? (B ? C $< A8gTJ  
A ? (B ? C : {-o7w0d_  
A ? (B ? C : D1) F='Xj@&O  
A ? (B ? C : D1) , #D= tX  
A ? (B ? C : D1) , D2 v$[ @]`  
A ? (B ? C : D1) , (D2 : EW ~*@H  
A ? (B ? C : D1) , (D2 : E ;:AG2zE!  
A ? (B ? C : D1) , (D2 : E : N{ : [/  
A ? (B ? C : D1) , (D2 : E : F1)  yQ<6p3  
A ? (B ? C : D1) , (D2 : E : F1) , F<h&3  
A ? (B ? C : D1) , (D2 : E : F1) , F2 Tq9,c#}&  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? P=f<#l"v  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? G NdM}xh  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? 2dv|6p  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H D9}d]9]$  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : eA~J4k_  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) ^fA3<|  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : } 9MW! Ss  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) UeMnc 5y  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : w;lx:j!Vp$  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : K )fxo)GS  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? D /,|pC  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? L 6&g!ZE'G  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? O{;M6U8C\  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M M3hy5 j(b  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : 7yY1dR<Y  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : N) 0|],d?-h  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : (N ? /AK*aRU^  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : (N ? O bl(BA}<  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : (N ? (O ? c28oLT1|D  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : (N ? (O ? P ^U0apI  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : (N ? (O ? P : / e~  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : (N ? (O ? P : Q) F!z! :yp  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : (N ? (O ? P : Q) : 5O;/ lX!u  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : (N ? (O ? P : Q) : R)) 8.Pcr<  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : (N ? (O ? P : Q) : R)) : D6C -x  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : (N ? (O ? P : Q) : R)) : S) Hl%+F 0^?  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : (N ? (O ? P : Q) : R)) : (S ? Mft0D j/  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : (N ? (O ? P : Q) : R)) : (S ? T *X-~TC0 [  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : (N ? (O ? P : Q) : R)) : (S ? T : [8V(N2  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : (N ? (O ? P : Q) : R)) : (S ? T : U)) -~rr<D\  
A ? (B ? C : D1) , (D2 : E : F1) , (F2 ? (G ? H : I) : J) : (K ? (L ? M : (N ? (O ? P : Q) : R)) : (S ? T : U)); !Szgph"ul  
为了便于观察,我们简写出最终识别结果: 6E*Zj1KX  
return A ? (B?C:D1),(D2?E:F1),(F2?(G?H:I):J)) : (K?(L?M:(N?(O?P:Q):R)):(S?T:U)); ~2, wI<Nz  
现在整个程序除了宏定义,字符串声明,函数头之外就只剩下这一句有效代码了! SdEb[  
再次验证我们分析的结果:编译,运行...与原程序完全相同。 ql%]$`IV6  
下面我们开始分析具体的语句: ^)eessZ  
A                   // !0<t 为假 ,故然后执行 (K(LM(N(OPQ)R))(STU)) F M@W>+  
K                   // t<0 为假 ,故然后执行 (STU) 1xFhhncf  
S                   // 0<t   为真,故执行 T .@x"JI> ;  
T                   // main(2,2,"%s")递归调用自身,表达式的值为函数返回值,现在无法确定, Y(Z(dV!Po  
//进入main(2,2,"%s") 将此处标记为一号位置 2WbZ>^:Nsk  
A                   // 1<2 为真,故执行 (B?C:D1),(D2?E:F1),(F2?(G?H:I):J)) 这是一个逗号表达式,执行次序是从左向右依次求值, +v`^_  
                  // 最终取值却是最后一个逗号表达式的值,即 (F2?(G?H:I):J))的值,虽然前面的表达式的值并不被引用,但仍然要执行 H|(*$!~e  
//故先进入 (B?C:D1) 将此处标记为二号位置 T$>=+U  
B                   //   t<3 为真,故执行 nvUkbmZG#  
C                   //   main(-79,-13,a+main(-87,1-_,main(-86,0,a+1)+a)),首先,这里再次递归调用自身,表达式的值为函数返回值,现在无法确定,将此处标记为三号位置 o/i5e=9[y  
                  //其次,在递归调用的同时,传递参数分别又调用两次自身,而C语言的函数参数是从右向左入堆栈,所以先调用 main(-86,0,a+1) N2S!.H!Wz  
//下面进入main(-86,0,a+1) 将此处标记为四号位置 a*$1la'Uf  
A                   //   1<-86为假 c,[qjr#\>  
K                   //   -86<0为真 #h5Hi9LKf  
L                   // -86<-72为真 6|(7G64{  
M                   // 调用 main(_,t,STRB),即main(0,-86,STRB)  onS{  
//进入 main(0,-86,STRB)   将此处标记为五号位置 8Vg`;_-  
A                   // 1<0为假 ggYIq*4  
K                   // 0<0为假 < 8W:ij.`  
S                   // 0<0为假 ,2:L{8_L  
U                   // *a此时为'@',故为假,那么继续判断 main(0,main(-61,*a,STRD),a+1),此过程又将进入 main(-61,*a,STRD) |[n|=ORI'  
//进入main(-61,*a,STRD),即使 main(-61,'n',STRD) 注意参数的运算顺序,先调用了a+1,然后才调用此函数,经过此函数,a已经指向STRD了 4HYH\ey  
A                   // 1<-61为假 ,y{fqa4  
K                   //   -61<0为真 GmWQJYX\  
L                   // -61<-72为假,那么继续 (N(OPQ)R) I]S(tx!  
N                   // -61<-50 为真,那么继续 (OPQ) UVuuIW0k  
O                   // 变量'_'此时的值为'n',而*a为'!',故为假 Q,LWZw~"  
Q                   //   调用main(-65,_,a+1)       [>QsMUvak  
//进入main(-65,_,a+1),即main(-65,'@','e') , &G5I0:a   
A                   // 1<-65为假 oFx gR9  
K                   // -65<0为真 =GS_ G;Dz  
L                   // -65<-72为假 uZ{xt6 f  
N                   // -65<-50为真 9~ .BH;ku  
O                   // 为假 T?$?5  
Q                   //调用 main(-65,_,a+1) a/xCl :=8q  
//进入main(-65,_,a+1),即main(-65,'@','k'),这里可以看到,每调用一次Q,a指针向后移动一次,直到a指向STRD中的@ '{_tDboY  
//中间过程不用再重复了,我们直接考虑当a指向STRD中的@时的情况: peP:5WB  
A                   // 9<#R;eIsv  
K                   // !ilDR<  
L                   // _rWXcK3cjr  
N                   // Wh&Z *J  
O                   // 此时终于为真,那么调用P I,q~*d  
P                   // putchar(31[a]);这是什么意思呢?其实数组在编译的过程是转换为指针运算的,31[a]也就相当于a[31] ,而a此时所指的 3uiitjA]  
                      //位置是STRD中的'@',相当于a[0],那么,a[31]就是 'O',到这里终于该从层层的递归中返回了,那么,返回值是什么呢? S)1:*>@  
                      //返回值就是这里最后的表达式P的值,也就是putchar()函数的返回值,它返回什么呢?返回输出的字符的ASCII码,也就是79 +hYmL Sq  
//一层层返回,每次返回值都是79,那么这个79最终到达哪里了呢? 对,返回到五号位置的U语句了 A}+r;Y8[h  
//那我们继续 main(0,-86,STRB) 的U语句: "e?#c<p7  
U //   main(0,main(-61,*a,STRD),a+1)即可变成   main(0,79,'n'),很不幸,我们需要继续判断这个函数的返回值,这里标记为五号位置 p~y 4q4  
//进入 main(0,79,'n') /IR5[67  
A   // 1<0为假 vTa23YDW  
K   // 0<0为假 206jeH9  
S   // 0<0 为假 #k]0[;1os  
U   // 非常不幸,又是U ! 这个时候参数a是多少呢?对了,a 的值其实在上面已经计算并且入栈了,所以还是'n'  Hqy>!1 !  
   // 还是不等于'/' ,那么,还要重复上面的过程!其实我们已经可以找到点规律了: }C1}T}U  
                      // 这次进入main(-61,*a,STRD)函数又会输出什么呢? 可以看出,传递进去的参数a又重新指向STRD的首地址了, r/w@Dh]{_  
                      // 这次是一直循环到等a指向 'n'的时候,再次后移31位并输出,我们可以算出,这次应该输出 'n',并返回'n'的ASCII码110 {nWtNyJpS  
其实,分析到这里我们就可以大概了解整个代码的运行过程了,可以看到,程序中输出语句只有一个putchar(),然而,用./a.out | wc统计得到,该程序 &l)v'  
一共输出字符数是2358个!!!那么可以想到,要输出完这些字符,函数至少递归了2358次!(其实不止这个数)考虑到分析该程序的目的“just for fun”, Kct +QO(  
我们就到此为至了吧! F>nrV  
经过分析,我们不由得不佩服这段代码的作者,虽说这样的代码风格是绝对不提倡的,但能设计出这样的代码,不能不说是种“强悍”! B}bNl 7 ~  
  ) x $Vy=  
7"U,N;y  
描述
快速回复

如果您提交过一次失败了,可以用”恢复数据”来恢复帖子内容
 回复后跳转到最后一页