为人戒骄戒躁,网络生活也理应如此!
二十年余了,期间很少上CSDN问答区,直到前段时间发现自己的一个学生在问答区成长很快。感觉问答板块设计还算合理,可以帮助更多真正需要帮助的人,答题过程也算寓教于乐。
但是,刚上来不久,就发生下面丢人的一幕。为此纪念和自我诫勉,特发此文!同时,也在此感谢网友贵阳老马马善福专业维修游泳池堵漏防水工程
的贡献。
【问题原文】
关于printf中的参数的计算次序的问题。
请看下面的程序:
int main(int argc, char* argv[])
{
int j;
j=10;
printf(" %d %d \n",j=2*3,j*5);
j=10;
printf(" %d %d \n",(j=2*3,j*5),j*8);
j=10;
printf(" %d %d \n",(j=2*3,j*5),j*8,j=100);
return 0;
}
// 输出分别为
6 50
30 48
500 800
十分不解,请高手指教,谢谢。
【网友 贵阳老马马善福专业维修游泳池堵漏防水工程 的回答】
函数的参数的求值顺序,是
未定义行为
,也就是说,不同的编译器编译产生的代码,结果不同。
当你的参数是副作用表达式的时候,则相同的源代码,编译出来的程序,结果不可控,因此这种写法要杜绝。
作为学校老师,让学生研究这个或者以此出题分析运算结果纯属误人子弟
。关于未定义行为,参考:
https://blog.csdn.net/pegasuswang_/article/details/12872797
https://blog.csdn.net/baihacker/article/details/9204429
【丢人的评论】(特别是红色字体
部分)
发言人 | 评论 | 时间 |
---|---|---|
我 | 您称函数参数的求值顺序是未定义行为的依据是什么呢?您又如何确定的学校老师纯属误人子弟? | 大约 15 小时之前 |
我 | C/C++ 中函数不加说明默认调用约定均为 __cdecl,参数入栈顺序是确定的,不存在不同的编译器结果不同的问题 ,更不会是未定义行为 | 大约 15 小时之前 |
我 | 不过有一点您说的很正确,参数是副作用表达式的时候,则可能是未定义行为,最好杜绝这种写法。但是这与提问者关于函数参数求值顺序的问题是无关的 | 大约 15 小时之前 |
贵阳老马 | 我想我已经解释很清楚了 | 大约 14 小时之前 |
贵阳老马 | 如果你还有什么不理解的,你可以重新开一个问题来讨论。谢谢合作。 | 大约 14 小时之前 |
我 | 您可以查阅ANSI或GNU C/C++标准,看看“未定义行为”undefined behavior 是什么意思,与函数参数求值顺序有无关系 | 大约 4 小时之前 |
贵阳老马 | 你的讨论内容和这个问题无关,所以建议你单独开一个问题我来给你解释。 | 大约 4 小时之前 |
贵阳老马 | 如果你有技术问题欢迎讨论,但是请不要蹭别人的问题,这是不友善的,也是csdn规则不允许的。谢谢合作。 | 大约 4 小时之前 |
我 | 蹭问题?不友善?跟您有这必要吗?我们的目的是纠正错误、查漏补缺,顺便帮助更多的人 | 大约 3 小时之前 |
我 | 如果您说函数值参的求值顺序是未限定行为(unspecified behavior),无意见,毕竟不同编译环境缺省调用约定可能不同,但是注意这不是由错误引起的,对其学习研究是有意义的。但是您坚持函数值参求值顺序是未定义行为(undefined behavior),则明确不对,因为C/C++标准对函数值参传递顺序是有明确描述的,编译器必须按预先描述做出响应,意思是说任何编译器在指定调用约定下的编译结果都是一样的 。 | 大约 3 小时之前 |
贵阳老马 | 你这就是无理取闹了 | 大约 3 小时之前 |
我 | 好吧,算我失言了。您好之为止吧 | 大约 3 小时之前 |
我 | 另外,我也是一名人名教师。但是对您说的误人子弟持保留意见,也希望提问者真正学有所长 | 大约 3 小时之前 |
贵阳老马 | 作为“人名(民)教师”,你应该懂得课堂纪律和秩序,如果你是语文教师,你会允许同学在课堂上讨论数学问题么?我已经说得很清楚,如果你有别的问题,单独开列出来讨论,而不是去蹭别人的问题,这是起码的秩序。明白? | 大约 3 小时之前 |
贵阳老马 | 如果学生认为你作为语文老师不允许课堂上讨论数学问题是剥夺了他“求知”的权利,这就是一种无理取闹。 | 大约 3 小时之前 |
我 | 我所说的错误,是“函数的参数的求值顺序,是未定义行为”是不正确的。相反,有却别与未定义行为的错误,函数值参求值顺序在任何编译环境编译结果都是已知和可控 的。这种学习是有意义和肯定的 | 大约 3 小时之前 |
贵阳老马 | 看我追加的回答。最后提醒一次,若再有问题,请另外开贴讨论。否则做直接删除处理。不再回应。 | 大约 2 小时之前 |
我 | 钻牛角?不是的,更没必要。当然的,你也不完全错:值参表达式可能是未定义行为,而值参求值顺序则属于未限定行为,区别两者将决定学习研究的必要性。 | 2 分钟之前 |
我 | 我之前的回复也有错误。重新翻查标准,发现您昨天说的“函数值参求值顺序与参数压栈无关”是正确的。值参求值早于压栈(这也是我谬解之处),并且标准没有明确规定值参的计算顺序,而是取决于编译器的实现。学无止境,与君共勉 | 2 分钟之前 |
注:贵阳老马,是指网友贵阳老马马善福专业维修游泳池堵漏防水工程
【丢人的回答】
(在网友的友情提醒下,发现我之前的回复是错误的。目前已更正,感谢贵阳老马马善福专业维修游泳池堵漏防水工程的贡献!)
1)函数值参求值因为早于参数表压栈,并且值参的计算顺序在C/C++标准中没有明确规定,因此取决于编译器的实现。即不同编译器的计算顺序存二义性,因此应尽量避免使用side effect副作用表达式作为函数参数。
【之前的答案,也是错误的答案!】1)C/C++默认函数参数表继承了汇编的栈操作,且值参为运行时动态求值。因此编译器按,运行时动态从函数调用堆栈中依次解析传入的参数,即从右至左顺序执行。
2)逗号表达式是一种运算符,返回值为最后一条子语句的值,并且逗号表达式的结合性为L-R,即从左至右的顺序执行本条语句。