鱼C论坛

 找回密码
 立即注册
查看: 2523|回复: 7

[技术交流] 由i++和++i引起一道问题的思考

[复制链接]
发表于 2016-8-24 10:20:22 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
相信很多人遇到过这样的问题:printf(“%d %d”,i++,++i);

也纠结过这个问题,到底答案是什么。确没有一个参考的资料。唯一知道的是,几乎所有C语言教材都这么讲:i++就是先使用i的值再使i自身加一,而++i则是先使i自身加一,然后在使用i的值。出于对真理的追求。今天我们彻底弄明白此问题。



譬如这样的话:

int a,b;

int i=10,j=10;

a=i++;

b=++j;

我们可以很清楚的知道a和b的值分别将是10和11。这点毫无疑问,因为无论在任何平台任何编译器上运行都是这个结果!

然而对于这样的程序:

int a,b;

int i=10,j=10;

a=(i++)+(i++)+(i++);

b=(++j)+(++j)+(++j);

各位试想答案将是多少?

我们可以放到编译器上运行看一下结果如下:

先看看windows下常用的VC6结果:

<图片在下边>



恩看到了,是30和37!嗯,但..这个结果好像有点怪~

那再看看Linux下gcc的结果:

<图片在下边>



哦,竟然也是30  37 。

那我们再看看古老一点的TurboC的结果:

<图片在下边>



结果成了30  39  , 喔~还真有点怪。

这是怎么回事呢?不同的编译器结果还不一样呢?



要说起这其中的原因,我们要先明白两个知识点。即“副作用”与“顺序点”。

这里我们引用《C Primer Plus》的说法:

“现在我们再讨论一些C的术语。副作用(side effect)是对数据对象或文件的修改。例如,语句:

states = 50;

的副作用是将变量states的值设置为50。这是副作用?这看起来更像是主要目的!然而,从C的角度来看,主要目的是对表达式求值。给C一个表达式4+6,C将计算它的值为10。给C一个表达式states=50,C将计算它的值为50。计算这个表达式的副作用就是把变量states的值改变为50。跟赋值运算符一样,增量运算符和减量运算符也有副作用,它们主要由于副作用而被使用。

          一个顺序点(sequence point)是程序执行中的一点;在该点处,所有的副作用都在进入下一步之前被计算。在C中,语句里的分号标志了一个顺序点。它意味着在一个语句中赋值运算符、增量预算符及减量运算符所做的全部改变必须在程序进入下一个语句前发生。任何一个完整的表达式的结束也是一个顺序点。

什么是完整的表达式呢?一个完整的表达式(full expression)是这样一个表达式----它不是一个更大的表达式的子表达式。完整的表达式的例子包括一个表达式语句里的表达式和在一个while循环里作为判断条件的表达式。

顺序点帮助阐明后缀增量动动作何时发生。例如,考虑下面的代码:

while(guests++<10)

          printf(“%d\n”,guests);

有时C的初学者会设想在本程序中“先使用该值,然后增加它的值”的意思是在使用printf()语句后在增加guests的值。然而,因为guests++<10是while循环的判断条件,所以它是一个完整的表达式,这个表达式的结束就是一个顺序点。因此,C保证副作用(增加guests的值)在程序进入printf()前发生。同时使用后缀形式保证了guests在于10比较后才增加。

现在考虑这个语句:

Y=(4+ x++)+(6+ x++);

表达式4+x++不是一个完整的表达式,所以C不能保证在计算子表达式4+x++后立即增加x。这里,完整表达式是整个赋值语句,并且分号标记了顺序点,所以C能保证的是在程序进入后续语句前x将增加两次。C 没有指明x是在每个子表达式被计算后增加还是在整个表达式被计算后增加,这就是我们要避免使用这类语句的原因。 ”



这是《C Primer Plus》的说法,相信您应该有一定答案了。

没错,那就是对于i=10;(++i)+(++i)+(++i);这样的语句。C语言标准并没有作规定。有的编译器计算出来是39,因为会使i的值自增三次变为13,然后使用增加三次之后也就是13的3个值相加为39。而有的编译器计算结果则为37,如VisaulC++6.0则会先计算前两个i的值为12,第三个i的值变成了加三次以后的值为13,因此结果是12+12+13=37。如果有心的话,您可以分别在VC6和TC上本别测试;(++i)+(++i)+(++i) +(++i)的值来洞悉不同编译器的处理规则。那么,回到最初的printf的问题,明白求值的顺序之后,再来看printf的求值问题,printf的参数都是从左到右依次压入栈内,所以计算起来求值运算的时候则是由右至左(栈的特点:即先进后出),那么至此,想必您已经完全想明白了这类问题的全部了!

所以讲到这里,想必大家就清楚缘由了,不同编译器的处理过程是不同的。所以并没有唯一的标准答案!现在大家明白了吗?
20.jpg

点评

我很赞同!: 5.0
我很赞同!: 5
  发表于 2016-8-24 10:35

评分

参与人数 1荣誉 +5 鱼币 +5 贡献 +5 收起 理由
zlh + 5 + 5 + 5 感谢楼主无私奉献!

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2016-8-24 19:43:35 | 显示全部楼层
强力支持楼主!!!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-12-19 10:00:11 | 显示全部楼层
本来这类语句就应该尽量避免使用,也就是考试时候回会考,写程序时候基本不用
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-12-19 17:18:03 | 显示全部楼层
老谭的书害人啊,就知道讨论这些无用的东西
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-4-7 20:18:40 | 显示全部楼层
2技能封走位 发表于 2019-12-19 17:18
老谭的书害人啊,就知道讨论这些无用的东西

很有用好吧..
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-4-10 21:53:49 | 显示全部楼层

谁写程序会写三个连续的i++,这不仅难于理解,也会随着标准,编译器的改变而改变,与其研究这些奇技淫巧,不如好好学习算法,如果你真的觉得大师的书好的话,那你感觉韩好强的书看完能编写出什么程序,老韩的书不仅代码风格糟糕,像函数声明在主函数,命名规则杂乱,主流的匈牙利命名法,下划线命名法还有驼峰命名法都未使用,书中更是有着大量的错误,等你c语言代码量达到5w行时可以有较深体会
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2020-4-12 20:51:29 | 显示全部楼层
2技能封走位 发表于 2020-4-10 21:53
谁写程序会写三个连续的i++,这不仅难于理解,也会随着标准,编译器的改变而改变,与其研究这些奇技淫巧 ...

某些考试可能会考 虽然实际根本用不到
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-1-15 06:45

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表