有人可以向我解释为什么这个代码打印14?我刚刚被另一名学生问过,无法弄明白.
int i = 5; i = ++i + ++i; cout<
phihag.. 44
副作用的顺序在C++中是未定义的.此外,在单个表达式中修改变量两次没有定义的行为(参见C++标准,§5.0.4,物理页面87 /逻辑页面73).
解决方案:不要在复杂表达中使用副作用,不要在简单表达中使用多个副作用.并且启用编译器可以提供的所有警告并没有什么坏处:向命令行添加
-Wall
(gcc)或/Wall /W4
(Visual C++)会产生一个拟合警告:test-so-side-effects.c: In function 'main': test-so-side-effects.c:5: warning: operation on 'i' may be undefined test-so-side-effects.c:5: warning: operation on 'i' may be undefined显然,代码编译为:
i = i + 1; i = i + 1; i = i + i;
小智.. 16
这是未定义的行为,结果将根据您使用的编译器而有所不同.例如,请参阅C++ FAQ Lite.
1> phihag..:副作用的顺序在C++中是未定义的.此外,在单个表达式中修改变量两次没有定义的行为(参见C++标准,§5.0.4,物理页面87 /逻辑页面73).
解决方案:不要在复杂表达中使用副作用,不要在简单表达中使用多个副作用.并且启用编译器可以提供的所有警告并没有什么坏处:向命令行添加
-Wall
(gcc)或/Wall /W4
(Visual C++)会产生一个拟合警告:test-so-side-effects.c: In function 'main': test-so-side-effects.c:5: warning: operation on 'i' may be undefined test-so-side-effects.c:5: warning: operation on 'i' may be undefined显然,代码编译为:
i = i + 1; i = i + 1; i = i + i;
2> 小智..:这是未定义的行为,结果将根据您使用的编译器而有所不同.例如,请参阅C++ FAQ Lite.
3> Michael Burr..:在一些答案/评论中,已经讨论了"未定义行为"的含义以及是否使程序无效.所以我发布这个相当长的答案,详细说明标准用一些注释说的内容.我希望它不是太无聊......
标准的引用位来自当前的C++标准(ISO/IEC 14882:2003).C标准中有类似的措辞.
根据C++标准,在一组序列点中多次修改一个值会导致不确定的行为(第5节第4段):
除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的评估顺序以及副作用发生的顺序是未指定的.5)在前一个和下一个序列点之间,标量对象应该修改其存储值最多一次通过表达式的评价.此外,只能访问先前值以确定要存储的值.对于完整表达式的子表达式的每个允许排序,应满足本段的要求; 否则行为未定义.[例:
i = v[i++]; // the behavior is unspecified i = 7, i++, i++; // i becomes 9 i = ++i + 1; // the behavior is unspecified i = i + 1; // the value of i is incremented- 末端的例子]
请注意,
i = 7, i++, i++;
由于逗号运算符是序列点,因此定义了第二个示例" ".以下是C++标准所说的"未定义行为"的含义:
1.3.12未定义的行为[defns.undefined]
行为,例如在使用错误的程序结构或错误数据时可能出现的行为,本国际标准没有规定任何要求.当本国际标准忽略对行为的任何明确定义的描述时,也可能预期未定义的行为.[注意:允许的未定义行为范围从完全忽略不可预测的结果,在翻译或程序执行期间以环境特征的文件方式表现(有或没有发出诊断消息),终止翻译或执行(发布诊断信息).许多错误的程序结构不会产生未定义的行为; 他们需要被诊断出来.]
换句话说,编译器可以自由地做任何想做的事情,包括
吐出错误信息,
做一些实施定义和记录,
有完全不可预测的结果
第二个项目涵盖了大多数编译器所具有的语言扩展,但当然没有在标准中定义.
所以我认为严格来说,表现出未定义行为的东西并不是"非法的",但根据我的经验,只要C/C++程序中有某些东西表现出"未定义的行为"(除非它是一个扩展) - 这就是一个错误.我认为将这种结构称为非法不会造成混淆,误导或误导.
此外,我认为试图解释编译器为达到值14所做的工作并不是特别有用,因为它忽略了这一点.编译器几乎可以做任何事情; 实际上,当编译器使用不同的优化选项运行时可能会达到不同的结果(或者可能产生崩溃的代码 - 谁知道?).
对于那些想要一些额外的引用或对权威的吸引力的人,这里有一些指示:
Steve Summit(comp.lang.c常见问题解答的维护者)从1995年开始对这一主题进行了长时间的回答:
http://www.eskimo.com/~scs/readings/undef.950321.html
以下是Bjarne Stroustrup就此事所说的话:
http://www.research.att.com/~bs/bs_faq2.html#evaluation-order
脚注:C++标准恰好使用"非法"一词 - 描述C++和标准C之间关于使用
static
或extern
使用类型声明的区别.