序列点、函数调用与未定义行为:为何i+++i++未定义而f(i)+f(i)却不是?
为什么
i++ + i++是未定义行为而f(i)+f(i)不是? 示例代码与编译命令
代码文件main.cpp
const int& f(int& i ) { return (++i);} int main(){ int i = 10; int a = i++ + i++; // 未定义行为 int b = f(i) + f(i); // 无未定义行为 }
编译命令
$ g++ main.cpp -Wsequence-point
核心原因:序列点(顺序规则)的差异
1. int a = i++ + i++; 触发未定义行为
C标准明确规定:如果在**没有序列点(C11及以后为“顺序规则”)分隔**的情况下,对同一个对象同时进行多次修改,或者修改与读取操作重叠,就会产生未定义行为。
i++是后置自增操作,逻辑是先读取i的当前值用于表达式计算,再将i的值加1。在i++ + i++这个表达式里,两个i++操作之间没有任何序列点:编译器既可以选择先读取两次i的值再执行两次自增,也可以先执行一次自增再读取第二次i的值——标准没有规定具体顺序,不同编译器甚至同一编译器的不同优化等级都可能给出不同结果,这就是典型的未定义行为。
2. int b = f(i) + f(i); 无未定义行为
函数调用是C++中明确的序列点来源:每个函数调用的执行过程中,函数内部的所有副作用(比如++i对i的修改)都会在函数返回前完全完成,函数调用结束就是一个序列点。
对于f(i)+f(i)来说,不管编译器选择先执行左边的f(i)还是右边的f(i),每个f(i)调用内部的++i操作都会在该函数返回前完成,不会出现“同一个对象在无序列点下被多次修改”的情况。无论调用顺序如何,最终的计算结果都是确定的(两次自增后的数值相加),因此不存在未定义行为。
内容的提问来源于stack exchange,提问作者Vivek
相关产品推荐
相关产品推荐

