分享
 
 
 

一段有趣的C程序

王朝c/c++·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

一段有趣的C程序

最近在一个论坛发现了一段简单的C程序很有意思,其中蕴含着不少技巧,在此与大家分享一下。

原程序如下:

#include <stdio.h>

main(_){char*x="*b#**000**I#*******2*0***#-.****5.*-#-.****54.#*******2**6#****00**0.#";while(_=*x/4)_-=8,printf("\n%*s"+!!_,_+_,"_/_/_/"+*x++%4*2);}

表面看起来很奇怪的代码,我把它放到VC++6下面编译,发现不能通过,提示”_”未声明,对以上代码稍加修改,如下:

#include <stdio.h>

main(int _){char*x="*b#**000**I#*******2*0***#-.****5.*-#-.****54.#*******2**6#****00**0.#";while(_=*x/4)_-=8,printf("\n%*s"+!!_,_+_,"_/_/_/"+*x++%4*2);}

细心的人已经看出来了,就是在main函数的参数”_”前面加上了变量的类型:int。此时,以上代码在VC6中就能够正常的编译和连接了。运行的结果如下:

_/ _/

_/ _/ _/_/_/ _/_/_/ _/_/_/ _/ _/ _/_/

_/ _/ _/ _/ _/ _/ _/ _/ _/ _/_/_/ _/ _/ _/

_/_/ _/ _/ _/ _/ _/ _/_/ _/ _/ _/_/

_/_/ _/ _/ _/ _/ _/ _/_/ _/_/_/ _/

_/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/

_/ _/ _/ _/ _/_/_/ _/_/_/ _/ _/ _/_/_/ _/

是用”_/”组成的单词“knocker”。

下面我们来分析这个小小的程序,不过为了看起来直观,我把这个程序稍加改动:

#include <stdio.h>

main(int _){

char*x="*b#**000**I#*******2*0***#-.****5.*-#-.****54.#*******2**6#****00**0.#";

while(_=*x/4)

{

_-=8;

printf("\n%*s"+!!_,_+_,"_/_/_/"+*x++%4*2);

}

}

首先来看看main函数参数,这个参数的名称比较奇怪”_”,我们看着有些不习惯,但它确实是合法的变量名称。另外,本来我们常见的main函数一般都不带参数,如果有参数是因为程序希望处理命令行格式下运行这个程序所需要的参数,一般是这样的:main(int argc, char **argv),第一个参数argc表示参数表中参数的个数,argc是一个char型二维数组,保存着参数字符串。举个例子,如果我们在命令行下输入命令:dir –s c:

此时,argc的值为3(包括命令本身),argv[0]指向字符串:dir,arg[1] 指向字符串:-s,arg[2]指向字符串c:\。那么既然main函数的参数通常要不没有,要不就是两个,而这个程序只有一个,这样做是否合法呢?答案是肯定的!我在《关于C语言中的变量》中提到过,函数中的参数是保存在堆栈中的,所以这就涉及到一个谁来平衡堆栈的问题,是调用者还是被调用者。在使用VC6编译器的时候,如果函数没有特别的声明,默认是调用者清理堆栈。换句话说,运行时库(runtime)调用main函数的时候,只传递一个参数给main函数,这一点它自己是知道的,当main函数调用结束以后,它在平衡堆栈的时候,只清除掉一个函数,因此不会出现任何问题。

我们接着往下看,下面定义了一个char型指针变量x,指向一个字符串。接下来是while语句,while(_=(*x)/4),但表达式_=(*x)/4的值不为0的时候就执行while的循环体,然而表达式_=(*x)/4是一个赋值语句,它的值又是多少呢?在这种情况下,通常赋值号右面的表达式的值就是整个表达式的值。第一次执行这个语句的时候,x指向字符’*’,对应的值为42,以此类推,当x=0时,也就是字符串结束的时候,循环结束。

接下来在原来的程序中是:_-=8,printf("\n%*s"+!!_,_+_,"_/_/_/"+*x++%4*2);这里的逗号作用有些类似分号,但是逗号两侧组成的是一条语句,而分号则是两条语句。

我们仔细看printf这条语句,它的格式字符串为:”\n%*s”,其中%*s很少见,很多人不知道这是什么格式,不过我们可以在MSDN中找到这样的描述:

If the width specification is an asterisk (*), an int argument from the argument list supplies the value. The width argument must precede the value being formatted in the argument list.

意思是说如果宽度用星号(*)来指定,则应该在参数列表中提供一个int型参数作为宽度的值。这样就清楚了,原来这个printf语句是用表达式_+_来控制字符串”%*s”的宽度,也就是替换其中的(*)。

在仔细一看,原来格式字符串还没有完!完整的应该是:"\n%*s"+!!_。这就奇怪了,字符串怎么和!!_相加呢?其实也不奇怪,在这里!!_是对变量_作了两次“非”的操作,结果应该是0或1。要明确的是,字符串作为参数传递给函数的时候,只是把字符串的首地址传递给了函数,所以字符串"\n%*s"的首地址就是字符’\n’的地址,当这个地址加1的时候,传递给printf函数的格式字符串就变成了"%*s",所以这个+!!_的奥妙就在于控制换行!是不是很有创意?

当然了,下面的代码同样有创意,就是printf语句要打印的字符串参数:"_/_/_/"+*x++%4*2。经过刚才的分析,我们不难理解"_/_/_/"加上后面的表达式的用意了,同样是控制输出字符的个数,是一个”_/”,两个,还是三个。那么关键就是后面这个表达式了:*x++%4*2。其实慢慢的分析也不困难,先执行*x%4*2,然后执行x++。所以,当在执行while语句的时候,x会指向下一个字符!

综上所述,这个小程序就是利用一个字符串来控制输出的例子,里面运用了很多技巧,对于初学者需要很好的理解,对今后的编程是很有帮助的!

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有