C语言88年乱码大赛经典作品完全解析
来源:互联网 发布:剑3成女捏脸数据 编辑:程序博客网 时间:2024/04/30 14:49
都说西藏是一种病,对于一个c程序员来说,我觉得乱码大赛的经典之作也是一种病,不把他完全理解通透总是不自在。至少对于我来说,是这样的。前前后后这是第三次看这份代码,第一次时候刚在学校学完C语言,只是下载到运行了一把,想看懂。结果看了一会。一脸懵逼的放弃。第二次拿起他,分析了一部分,只是简单的把每个三目运算符对号入座了一下。又放弃了。怂在了他69K+次递归和2.3K+次输出。这次,再次拿起这份代码。也想检验一下自己的逻辑能力。切入正题,开始分析这令我叹为观止的递归。
首先上第一道菜,这基本是没改动的原版,只是把其中一点不符合现在编程语法这类的东西做了修改,至于原版,网上多的是。
#include <stdio.h>main(int t,int _,char* a){ return !0<t?t<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? main(2,_+1,"%s %d %d\n"):9:16:t<0?t<-72?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' ')# }'+}##(!!/") :t<-50?_==*a?putchar(a[31]):main(-65,_,a+1):main((*a=='/')+t,_,a+1) :0<t?main(2,2,"%s"):*a=='/'||main(0,main(-61,*a, "!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m .vpbks,fxntdCeghiry"),a+1);}
这副样子一眼看过去是很头疼的,我们先来简单处理一下,把其中的字符串取出来,顺便把A?B:C
语句用缩进来整理下,对于短的直接放到一行,也容易看,对于长的,我采用下面这种格式:
A? //每个字母表示一个表达式 ( B ): ( C? ( D ): () )
上代码
#include <stdio.h> char *p1 = "%s %d %d\n";char *p2 = "@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' ')# }'+}##(!!/";char *p3 = "!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m .vpbks,fxntdCeghiry";main(int t,int _,char* a){ return 1 < t ? //!0就是真值,即 1 ,这里为了不迷惑,直接写成1 ( ( (t < 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? main(2,_+1,p1):9 ): 16 ) ) ) ): ( t<0 ? ( t<-72? ( main(_,t,p2) ): ( t<-50? ( _==*a?putchar(a[31]):main(-65,_,a+1) ): main((*a=='/')+t,_,a+1) ) ): ( 0<t? main(2,2,"%s"):*a=='/'||main(0,main(-61,*a,p3),a+1) ) );}
现在,至少看起来容易点了,但是实际的运行,也就是递归过程,还是一脸懵逼态。
接下来开始一步步分析,看程序是怎么递归的。
可以看到main带了三个参数,这是合法的,具体什么意思自行百度。这不是我们今天的重点。第一次进入时候不需要关注第三个参数,第一二个参数值分别是 1, 1. 开始判断
- 1 < 1不成立,跳到第33行,1 < 0 不成立,跳到第48行,0 < 1 成立,调用main(2,2,“%s”)
- 1 < 2成立, 跳到16行, 2 < 3 成立, 顺次进去调用 main(-86,0,a+1)
- 1 < -86不成立,跳到33行,-86 <0成立,跳到35行,-86 < -72成立,调用main(_,t,p2),即main(0,-86,p2)
1 < 0不成立,跳到33行, 0 < 0不成立,跳到48行,0 < 0不成立,判断*a == ‘/’,*a即p2[0],不成立,所以执行 || 后面的 main(-61,*a,p3) 接下来不具体说了直接说相应的行。这次递归跳到42行 后面的main,每次调用这个main p3加1,直到_==*a 即p2[0] == *a,也就是匹配到p3中第一个和p2[0]相等的字符,就输出这个字符后第31个字符
至此,终于把第一个字符分析出来了。
这里有一个重点地方,第一个是*a ==’/’ ||根据程序偷懒规则,当前面为真,或后面的不执行直接返回结果。所以仔细一分析可以看出,程序每次以“/”作为分割,顺次输出每段字符(哪些段后面说)。第二个就是
_
==*a?putchar(a[31]):main(-65,_,a+1),当_
==a[0]时候,程序会输出一个字符,简单分析一下不难发现,这里的两个字符引自的字符串是固定的,_
恒为p2,此处的a[0]恒为p3,也就是说,输出字符来自p3,顺序来自p2,我们写个简单的程序,把p3字符按照p2规定的顺序输出,并且以 ‘/’ 分割开。
第三,控制程序跳转循环的是第一第二个参数,控制程序输出的是第三个参数,记住这些,我们后面会用到
这是我的写好的代码#include <stdio.h>#include <stdlib.h>#include <string.h>char *p2 = "@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' ')# }'+}##(!!/";char *p3 = "!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m .vpbks,fxntdCeghiry";int main(){int i = 0,j = 0,len = -1;len = strlen(p3);while(*(p2+i) != '\0'){ if(*(p2+i) == '/') { printf("\n"); i++; continue; } for(j = 0;j < len;j++) { if(p3[j] == *(p2+i)) { printf("%c",p3[31+j]); break; } } i++;}return 0;}
结合上面两点重要信息,这里就是这段代码的27条 ‘短语’(为了描述方面,姑且这么叫吧)
再来对比一下原程序的输出。短语都能吻合上,证明我们的路是对的。
现在就走出了一大步了,至少,我们把无厘头的递归循环,简化成27条短语组合,接下来分析他们怎么组合的。
我们可以发现格式是这样的
A1 B1 A2 C1
A1 B2 A2 C2 C1
A1 B3 A2 C3 C2 C1
…
A1 B12 A2 C12 C11 …… C2 C1- 这里分析不在脑算了,直接单步调试看call Stack窗口,不难看出进入第17行第三个main控制输出了A1,第二个顺次输出了B1–B12,第三个输出了A2,第二个控制B的顺序,主要是用_,需要我们知道他的call by的参数值,第一第三固定值,可以略过了。 顺次执行完这三个main后,执行逗号表达式后面语句,
- 这里就是C系列的控制逻辑了。用第19行的main控制C的个数级顺次,20行的main调用输出字符,这里参数t和都用上了。用携带信息是输出几个C,t所标示输出C?(? in 1,2,3……12)
- 由于这里是与逻辑,前面部分永真语句,所以后面的必会执行。这里,也就是调用的总源头了t==2,第一次执行,我们手算分析那儿t是等于2的然后调用这里的。所以条件成立的,进去
_ < 13 这句话也就是12次递归循环的起源了。当_大于等于13后,后面只是常数直接返回,然后一步步结束这个递归。
好了。至此,我三次接触这段代码,总算有了一个善了的结果。写出来纪念一下,也分享给喜欢这段程序的你们。有什么疑问评论区交流。一起学习。
- C语言88年乱码大赛经典作品完全解析
- 国际C语言乱码大赛
- 国际C 语言乱码大赛(IOCCC)1988年获奖作品
- 国际C语言乱码大赛(IOCCC)经典之作
- 国际C语言乱码大赛(IOCCC)经典之作
- 1987年C语言乱码大赛之最优秀单行代码
- 国际C 语言乱码大赛(IOCCC)获奖作品
- 一步步走向国际乱码大赛-- 恶搞C语言
- 国际混乱C语言大赛获奖作品解析示例
- 国际混乱C语言大赛获奖作品解析示例(转载)
- C语言混乱大赛1987年获奖作品
- C语言大赛
- 混乱C大赛代码解析
- C语言大赛 第七题
- C语言 printf格式控制符 完全解析
- C语言 printf格式控制符 完全解析
- C语言 printf格式控制符 完全解析
- 1984年混乱C语言大赛程序分析
- 日志系统
- AutoCAD .Net 创建圆Circle
- [MySQL] CentOS7 yum安装mysql5.5/5.6并初始化
- 树莓派3下有VC程序设计的例子
- malloc()和free()
- C语言88年乱码大赛经典作品完全解析
- 大小写字母的转换
- 安装Redis
- MarkDown数学公式的详解
- Mac 更改jdk环境变量
- CSS3制作旋转的3D立方体
- mysql指定将某个字段更新到另一个表中
- input子系统基础之按键5——按键驱动
- 微信公众号开发以及代码菜单创建