Chinaunix

标题: 关于函数入参顺序 [打印本页]

作者: luoops    时间: 2013-05-03 17:32
标题: 关于函数入参顺序
无意中在网上看到一个问题:
int i = 9;
printf("%d %d %d\n", ++i, i, --i);

输出结果是:
9 9 9


很疑惑,为什么不是9 8 8?
作者: 井蛙夏虫    时间: 2013-05-03 19:50
c和c++中有sequence-point的概念,在两个sequence-point点之间多次修改同一个变量会导致未定义的行为。
分号就是一种sequence-point,printf语句中的两次修改i就处于同样sequence-point之间,所以结果是未定义的。

printf("%d %d %d\n", ++i, i, --i);之所以会产生“9 9 9”的结果,可能是因为前缀“++”或“--”相当于C++中返回i的引用,
编译器可能在计算完所有的参数后,才将参数复制到printf的实参位置,结果“++i”、“i”和“--i”都是从i的地址取i的最终结果传给printf。

如果将"++i"和"--i"改为“i++"和"i--",这个问题就没有了,因为后缀"++"和"--"必定会产生临时变量返回。但是中间的i的值还是有问题的,
它应当是最终的i值。

如果将"int i;"改为"volatile int i;"应该是没有问题的了。
作者: hellioncu    时间: 2013-05-03 20:21
这个结果不一定,跟编译器有关
作者: pmerofc    时间: 2013-05-04 15:33
提示: 作者被禁止或删除 内容自动屏蔽
作者: cxytz01    时间: 2013-05-04 17:51
请教楼主问什么是9 8 8, 我改成了volatile,结果确实是9 8 8。

回复 1# luoops


   
作者: lin5161678    时间: 2013-05-06 15:10
回复 4# pmerofc

你的赞同包不包括

如果将"int i;"改为"volatile int i;"应该是没有问题的了。

这句

加volatile 就没问题了吗
   
作者: pmerofc    时间: 2013-05-06 16:26
提示: 作者被禁止或删除 内容自动屏蔽
作者: lrita    时间: 2013-05-06 16:39
这种扯淡的东西还是别研究了。

作者: 井蛙夏虫    时间: 2013-05-06 19:44
回复 6# lin5161678
volatile只是保证传给printf的i参数使用计算它的时候的值,而不是被别的改变后的最终值。
它不能保证"++i", i 和 "--i"的计算顺序。


   
作者: luoops    时间: 2013-05-06 21:12
回复 9# 井蛙夏虫

C语言为了保证数目不定的参数功能,应该要求参数都是从右至左入栈吧?
所以计算顺序应该的固定的吧!

   
作者: luoops    时间: 2013-05-06 21:13
谢谢,明白了为什么是9 9 9!回复 2# 井蛙夏虫


   
作者: 井蛙夏虫    时间: 2013-05-06 23:34
回复 10# luoops
c语言的默认调用约定是规定了参数从右向左入栈,但入栈顺序不等于计算顺序。比如:
1. i -> eax; ++eax; eax -> i; i -> ebx; i -> ecx; --ecx; push ecx; push ebx; push eax;
2. i -> ecx; --ecx; ecx -> i; i -> ebx; i -> eax; ++eax; push ecx; push ebx; push eax;
上面两种情况入栈顺序都是从右向左,但计算顺序相反。
我刚用gcc和clang分别测试了你的例子(带volatile),gcc的计算顺序是从右向左,clang的计算顺序是从左向右。你可以测试一下。

你要观察不同调用约定下函数入参顺序,最好是用“gcc -S”或者“objdump -d”得到反汇编代码分析,或者gdb调试观察内存内容。

   
作者: lin5161678    时间: 2013-05-07 11:20
回复 7# pmerofc

同意
   
作者: lin5161678    时间: 2013-05-07 11:25
回复 10# luoops

入栈顺序 和 求值顺序 没什么联系的

对于你说的 从右向左入栈
可以是
从右向左全部求值完毕了 然后从右向左入栈
可以是
从左到右全部求值完毕了 然后从右向左入栈
可以说
随机高兴对那个求值就对那个求值 然后从右向左入栈
可以是从右边求值一个 就入栈一个
bbabababababababbababa........................................................

全废话
就一句 求值顺序不做保证 爱咋地咋地 入栈顺序和求值顺序不是一回事
作者: yulihua49    时间: 2013-05-07 13:39
luoops 发表于 2013-05-03 17:32
无意中在网上看到一个问题:
int i = 9;
printf("%d %d %d\n", ++i, i, --i);

没必要研究这个问题,实际上,你如果不想你的程序一塌糊涂,就别这么用。
作者: luoops    时间: 2013-05-08 08:56
如果是参数是函数呢?
比如func(func1(), func2());
func1和func2执行顺序也是不确定么?
回复 14# lin5161678


   
作者: yuccn    时间: 2013-05-08 09:32
反汇编一下
作者: shanehan    时间: 2013-05-08 09:43
这个的确是未定义行为,不同的编译器可能处理结果是不一样的,非要了解哪种编译器的结果是为什么,看反汇编加上举例测试,可以得出一些结论(注意不要仅以这一个例子去得出结论,因为通常这样得出的结论很可能是片面的)
作者: safedead    时间: 2013-05-08 09:56
luoops 发表于 2013-05-08 08:56
如果是参数是函数呢?
比如func(func1(), func2());
func1和func2执行顺序也是不确定么?



参数是函数的话有两种情况:
1、传入参数用的是函数的返回值,这个相当于用表达式作为函数入参,求值顺序是编译器决定的,一般情况下没固定顺序,不要依赖
2、传入参数是个函数指针,这个近似于函数内嵌套函数(函数嵌套不是C语言,我这里借用FORTRAN的概念),除非你知道函数如何实现,否则也无法判定调用顺序

总之,编程要尽量弱化顺序依赖,特别是在现代CPU和编译器体系上,某些编译器在多CPU在环境下的某些行为可以用疯狂来形容
作者: lin5161678    时间: 2013-05-08 10:49
回复 16# luoops

是的 对于 func(func1(), func2());
这里是先调用func1函数 还是先调用 func2函数
是无法确定的

作者: luoops    时间: 2013-05-09 16:42
回复 19# safedead
谢谢,大概明白怎么回事了!
BTW,你们是工作中总结出的经验还是有相关标准文档可以查阅啊?
关于C,我就看了谭浩强的C入门和《C和指针》,感觉很多东西似是而非,稍微深一点就不知道了!
十分感谢!


   




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2