VC++(Debug模式)与G++执行结果不一致,是否存在编译器Bug?
为什么VC++ Debug模式和G++执行同一段代码结果不同?
嘿,这个问题其实不是VC的编译器Bug,而是你碰到了C里一个很容易踩坑的点——未定义行为!不同编译器(甚至同一编译器的不同模式)对这类代码的处理可以完全不同,这都是标准允许的。
问题出在哪?
看你主函数里的这一行代码:
std::cout << t << '\n' << t++ << '\n' << t;
在C++标准里,像operator<<这种函数调用的参数求值顺序是未指定的。也就是说,编译器完全可以自由选择先计算哪个参数——是先算最左边的t,还是先算中间的t++,没有强制要求。
这就导致了两种编译器的差异:
- G++可能是按从左到右的顺序求值:先输出初始状态的
t({ 1,2,3 }),然后执行t++(返回自增前的临时对象{ 1,2,3 },同时t变成{ 2,3,4 }),最后输出已经自增后的t,所以得到你看到的结果。 - VC++ Debug模式下,它可能选择了另一种求值顺序:比如先处理所有需要读取
t的表达式,其中t++会先把t改成{ 2,3,4 },然后再依次输出各个值——所以第一个输出的就是已经自增后的t,然后是t++返回的临时对象,最后还是t,于是出现了{ 2,3,4 } { 1,2,3 } { 2,3,4 }的结果。
什么是未定义行为?
简单说,就是C++标准没有对这种操作的结果做任何规定,编译器想怎么处理都行——输出不同结果、程序崩溃、甚至做完全无关的事情,都是“合法”的。所以咱们写代码一定要避开这种情况。
怎么修复?
把这个复合表达式拆成独立的语句就行,这样求值顺序就完全明确了:
Test t{ 1,2,3 }; std::cout << t << '\n'; std::cout << t++ << '\n'; std::cout << t;
不管用VC还是G,这段代码的输出都会是:
{ 1,2,3 } { 1,2,3 } { 2,3,4 }
因为每个cout调用都是独立的语句,执行顺序严格从左到右,不会有参数求值顺序的歧义。
最后提醒
记住一个原则:不要在同一个表达式里既修改一个变量,又多次读取它(除非有明确的顺序点,比如分号、逗号运算符、函数调用的括号等),否则大概率会触发未定义行为,踩各种奇怪的坑。
内容的提问来源于stack exchange,提问作者Steve Wilson
相关产品推荐
相关产品推荐

