有人能向我解释为什么这段代码打印 14 吗?刚才有一个同学问我,我没能弄清楚。

int i = 5;
i = ++i + ++i;
cout<<i;
有帮助吗?

解决方案

C++ 中未定义副作用的顺序。此外,在单个表达式中修改变量两次没有定义的行为(请参阅 C++标准, ,§5.0.4,物理页 87/逻辑页 73)。

解决方案:不要在复杂的表达式中使用副作用,不要在简单的表达式中使用多个副作用。启用编译器可以给您的所有警告也没有什么坏处:添加 -Wall(海湾合作委员会)或 /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;

其他提示

这是不确定的行为,结果将取决于您使用的编译器的不同而不同。参见,例如, C ++ FAQ精简版

在一些答案/评论中,讨论了“未定义行为”的含义以及这是否会使程序无效。因此,我发布了这个相当长的答案,详细说明了标准的内容以及一些注释。我希望这不会太无聊...

该标准引用的位来自当前的 C++ 标准 (ISO/IEC 14882:2003)。C 标准中也有类似的措辞。

根据 C++ 标准,在一组序列点内多次修改某个值会导致未定义的行为(第 5 节第 4 段):

除非另有说明,否则 个体操作数的评估 运算符和子表达式 单个表达式和顺序 发生哪些副作用,是 未指定.53) 在前一个之间 下一个序列点是一个标量 对象应有其存储值 最多修改一次 表达式的计算。此外,先前的值应为 仅访问以确定值 要存储。这个要求 各款应满足 允许的顺序 完整表达式的子表达式;否则行为是未定义的。[例子:

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]

行为,例如在使用错误的程序构造或错误数据时可能出现的行为,为此 国际标准没有要求。当出现以下情况时,也可能出现未定义的行为 国际标准省略了对行为的任何明确定义的描述。[笔记:允许的未定义 行为范围从完全无视情况并产生不可预测的结果,到在 以记录的方式翻译或程序执行环境特征(有或没有 发出诊断消息),终止转换或执行(发出 诊断消息)。许多错误的程序构造不会产生未定义的行为;他们需要接受诊断。]

换句话说,编译器可以自由地做任何它想做的事情,包括

  1. 吐出一条错误消息,
  2. 做一些实现定义和记录的事情,
  3. 产生完全不可预测的结果

第二项涵盖大多数编译器具有的语言扩展,但当然没有在标准中定义。

所以我想严格来说,表现出未定义行为的东西并不是“非法的”,但根据我的经验,每当 C/C++ 程序中出现表现出“未定义行为”的东西(除非它是扩展)时,这就是一个错误。我认为称这样的构造为非法并不令人困惑、误导或误导。

另外,我认为尝试解释编译器为达到值 14 所做的事情并不是特别有帮助,因为这没有抓住重点。编译器几乎可以做任何事情;事实上,当编译器使用不同的优化选项运行时,它可能会得到不同的结果(或者可能产生崩溃的代码 - 谁知道呢?)。

对于那些想要一些额外参考或求助于权威的人,这里有一些提示:

Steve Summit(comp.lang.c 常见问题的维护者)从 1995 年起就这个主题给出了很长很长的答案:

以下是 Bjarne Stroustrup 对此事的看法:


脚注: :C++ 标准只使用了“非法”一词一次 - 在描述 C++ 和标准 C 之间关于使用的差异时 static 或者 extern 与类型声明。

简单......你编译器被评估的 BOTH 执行的总和,而不缓存中间结果之前增量。这意味着,当添加我两次,现在具有7的值。

如果您

int j=++i; 
int k=++i;

i = j+k;

你会看到13的预期。

在您的特定的编译器,它的选择先做两个++操作,然后加入。它的解释代码为:

int i = 5;
++i;
++i;
i = i + i;
cout << i;

这是完全有效的。

更好的问题是,它总是会这样吗? 14?

int i = 5;
i = ++i + ++i;
cout<<i;

i = ++i   + ++i   ;
i = ++(5) + ++(5) ;
i =    6  +    6  ;
i = 12;

i = ++i   + ++i   ;
i = ++i   + ++(5) ;
i = ++i   +   (6) ;
i = ++(6) +    6  ;
i =   (7) +    6  ;
i = 13;

i = ++i   + ++i   ;
i = ++i   + ++(5) ;
i = ++(6) +   (6) ;
i =   (7) +   (7) ;
i = 14;

很可能它会是 14, ,因为它使得 轻微地 更有意义。

我觉得看着从眼前语法树的问题时,回答该问题变得更加清晰:

我结果 |点击 =点击 |点击 +点击 |点击 一元的表达 - 一元表达式结果 点击 一元表达式:一元运算符表达式结果 点击 在我们的例子表达归结为变量i。点击 点击 现在发生的事情是,这两个一元表达式修改相同的操作,所以代码执行两次++同时添加一元表达式的结果之前评估一元表达式时我。结果 点击 那么代码的功能的确是点击 点击 ++ I;结果 ++ I;结果 I = I + I;结果 点击 对于i = 5,这意味着,点击 点击 I = I + 1; // I < - 6
I = I + 1; // I < - 7
I = I + I; // I < - 14

由于前缀增量的优先级:

int i = 5;
i = i+1; // First ++i, i is now 6
i = i+1; // Second ++i, i is now 7
i = i + i // i = 7 + 7
cout << i // i = 14
 i = i++ + i; //11  

 i = i++ + i++; //12

 i = i++ + ++i; //13

 i = ++i + i++; //13

 i = ++i + ++i; //14    
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top