转自http://hi.baidu.com/shiqicai/blog/item/a90ef8818fa1b4dcbd3e1e05.html
作者shiqicai
—写给不知道c/c++序列点的人
去年去蹭迅雷笔试,顺便把试卷给蹭出来了。最近拿出来看看,忽然发现有道题很奇怪。
虽然那张试卷错漏百出(如低级的印刷错误,二叉树遍历题居然没正确答案),但这道题却很有深度。
把c++里有深度的题出成选择题,或者出成一道错题又或者能展现出题者的bt程度。且看看这道题:
已知a=8,b=7,c=5,d=11
则(--a,b>a--&&c>d)?d++:a--的值为?
我当时想逗号表达式的值为最后一个表达式的值,那么应该执行--a(此时a=7)和b〉a--(只是判断b>a,a的值暂时不变)后,返回a--(a暂时还是7)的值。那么逗号表达式的返回值应该是7。最后做两次遗留的--运算,a变成5.但是选项里没有7这一项。不禁想起了这个很经典的例子:
int a=7;
printf("%d %d",a++,a++);
想必这个例子给大家都留下了很深刻的印象。似乎中国人编的c教材里都有这个例子,但是都没深入探讨这个问题。谭浩强说上面的语句行为在ansic里并未定义,也就是说不同的编译器可能给出不同的结果。不过他提到了在msc中会得到7 7的结果,在某些编译器中会得到7,8.这使我更加觉得题目本身答案就不确定,而如果作为选择题一定要给个答案7还是较好。
那么迅雷的这道题确实就是错题了么?最早引起我怀疑的是《c常见问题集》里面提到c是有序列点概念的。“序列点是一个时间点(在整个表达式全部计算完毕之后或在 ||、 &&、 ? : 或逗号运算符处, 或在函数调用之前), 此刻尘埃落定, 所有的副作用都已确保结束。”
接着,一本没怎么听说过的书《C++编程你也行》更是清晰的写到:“当逗号是序列操作符时,它会向代码中注入一个序列点,意味着逗号左边的表达式是完全求值的(fully evaluated),在触及右边的表达式之前所有副作用均已完成。”所以int a=7;printf("%d %d",a++,a++);和a=8,b=7,c=5,d=11;(--a,b>a--&&c>d)?d++:a--;不一样,前者中逗号是函数参数的分隔符,++的行为是未定义的,而后者中逗号是逗号运算符,所以在逗号和逻辑并(&&)处均存在序列点,那么答案就是确定的。--a后a的值为7,判断b>a,接着立即执行--运算,a=6.然后做a--:先返回a,再执行--。也就是说表达式返回值为6.
所以说出这道题的人还是很强大的。