问题描述
问题:为什么输出的y是5,而不是6?
相关代码
#include <iostream>int main() { int someValue = 1, x = 1, y = 5; std::cout << (someValue ? ++x, ++y : --x, --y) << std::endl;return 0;}
相关贴图(测试环境:Xcode 7.3.1 OSX EI Capitan)
个人思考
思考1:因为条件运算符?:是右结合,所以先执行--y,y变成4,再执行++y,y变成5。疑问:条件运算符的执行顺序是:先求解表达式1,若为非0(真)则求解表达式2,此时表达式2的值就作为整个条件表达式的值。那为什么还会出现这样的问题
思考2:这句语句中的第二第三个运算对象指向同一个对象x,y;而且两个都修改了对象的值,这种行为是未定义的。疑问:但是?:运算符不是已经确定了运算顺序码?为什么还会出现这样的问题?
问题解答
回答1:前言每次遇见问c++问题的,都是这样的比较基础的问题,这段话我每次都说:反汇编是c++程序员必掌握的一项技能!!!反汇编是c++程序员必掌握的一项技能!!!反汇编是c++程序员必掌握的一项技能!!!否则,在调试程序的时候就跟瞎子没什么两样,只能靠猜。
分析实际上根据经验我认为远没有那么复杂,更不是未定义行为,而是运算符作用域的问题。这个表达式正确的执行顺序如下:
if(1){ ++x;//这句没有用,在release模式下会被编译器优化掉 ++y;//y+1}else{ --x;}--y;//y-1cout<<y<<endl;
一目了然,因为 :运算符的优先级要高于,。而:之前的条件为真,只能顺序执行,而之后的,则超出的:运算符作用域。所以,无论条件成立与否,--y一定会被执行。所以正确写法应该加括号.
auto y=someValue ? (++x, ++y) : (--x, --y);//y=6反汇编
00F0625E mov dword ptr [someValue],1 00F06265 mov dword ptr [x],1 00F0626C mov dword ptr [y],5 00F06273 cmp dword ptr [someValue],0 00F06277 je main+56h (0F06296h) ;判断条件是否成立00F06279 mov eax,dword ptr [x] 00F0627C add eax,1 00F0627F mov dword ptr [x],eax 00F06282 mov ecx,dword ptr [y] 00F06285 add ecx,1 ;y的值+1变成了600F06288 mov dword ptr [y],ecx 00F0628B mov edx,dword ptr [y] 00F0628E mov dword ptr [ebp-0E8h],edx00F062A8 mov edx,dword ptr [y] 00F062AB sub edx,1 ;再减一变成500F062AE mov dword ptr [y],edx ;传值返回 回答2:
因为?:运算符的优先级高于,运算符,所以(someValue ? ++x, ++y : --x, --y) 这个表达式相当于 ((someValue ? (++x, ++y) : --x), --y) 所以条件运算++y之后会--y,所以y输出5
回答3:首先纠正一点,任何运算顺序都是有定义的。多数未定义行为是针对求值顺序而不是运算顺序。
c 中有定义的求值顺序有三个:A ? B : C,A, B, A && B(当然也有 ||)。你上面的代码可以被指责是可读性不好的,但是不能说是未定义的。
结果相当明确,就是 5。

