带赋值运算符的表达式求值顺序问题:为何代码运行结果z=20而非预期19
代码结果偏差的底层原因解析
涉及的原代码
#include <iostream> using namespace std; int main() { int y=3; int z = (--y) + (y=10); printf("%d \n", z); }
核心问题本质
这段代码属于C++标准明确规定的未定义行为,不存在唯一的正确输出,你推导的19和实际得到的20,都是不同编译器自由实现的结果,不属于逻辑推导错误。
预期输出19的推导前提
你默认遵守的规则是「加法运算符先计算右操作数,再计算左操作数」,推导流程为:
- 初始y赋值为3
- 先执行右操作数
y=10,将y的值修改为10 - 再执行左操作数
--y,将y的值减1得到9 - 最终加法结果为9 + 10 = 19
但这个求值顺序本身没有标准强制依据,不是编译器必须遵守的规则。
实际输出20的生成逻辑
对应编译器的实现属于未定义行为允许的自由优化,常见逻辑为:
- 先执行右操作数
y=10的赋值,y被修改为10 - 计算左操作数时,编译器直接读取当前y的值10作为
--y的返回值,甚至直接省略了前置自减的副作用(也就是没有实际执行y减1的操作) - 最终加法结果为10 + 10 = 20
底层语法规则说明
核心原因是C++的序列点规则:
C++标准明确规定:当对同一标量对象的两个未排序的副作用没有被序列点隔开时,程序行为是未定义的。
- 在C++17标准之前,若同一表达式中存在多个针对同一内存位置(这里是变量y的存储单元)的副作用(
--y会修改y,y=10也会修改y),且两个副作用之间没有序列点隔开,就属于未定义行为。 - 加法运算符
+本身不会引入序列点,也没有规定左右操作数的求值顺序,编译器可以自由重排操作、甚至省略部分合法的操作步骤,标准对这类未定义行为没有任何约束要求。 - C++17虽对部分运算符的求值顺序做了强制规定,但加法运算符的左右操作数求值顺序仍未明确,这种写法依然会触发未定义行为,工程中应避免编写这类存在歧义的代码。
内容的提问来源于stack exchange,提问作者Quang Thinh Ha
相关产品推荐
相关产品推荐

