1

如果我这样做,在 Java 和 C# 中:

int i=1;
int j= i++ + i;

j 是 3,即它转换为 1+2 并在加法之前增加 i。

然而,在 C 中 j 是 2,即它转换为 1+1 然后增加 i。

C 和 Java/C# 中导致表达式存在差异的内部机制是什么?

(后修复也是如此。Java/C# 变为 4,C 变为 3。)

谢谢。

顺便说一句,最初我认为这将是 C 的答案,因此对 Java/C# 结果感到困惑。

4

4 回答 4

5

与精确指定副作用发生时间的 Java 和 C# 不同,C 禁止在下一个序列点之前使用具有副作用的表达式。您的表达式不仅会在 C 中产生不同的结果,而且还是未定义的行为。

于 2013-02-20T13:45:15.670 回答
2

在 C 语言中,该机制是对编译器编写技能的精心调整,被亲切地称为未定义行为或“UB”。

换句话说,没有人可以为 C 提供答案,因为事先知道代码会触发未定义的行为。任何事情都有可能发生,没有“对”或“错”之分。

于 2013-02-20T13:44:52.090 回答
2

来自C 语言标准的 2011 年草案

6.5 表达式

...
2 如果标量对象的副作用相对于同一标量对象的不同副作用或使用同一标量对象的值的值计算是未排序的,则行为未定义。如果表达式的子表达式有多个允许的排序,则如果在任何排序中出现这种未排序的副作用,则行为未定义。84)

3 运算符和操作数的分组由语法指示。85)除非稍后指定,否则子表达式的副作用和值计算是无序的。86)
84) 本段呈现未定义的语句表达式,例如
    我=++我+1;
    a[i++] = i;
同时允许
    我 = 我 + 1;
    a[i] = i;

85) 语法规定了表达式求值中运算符的优先级,与本小节主要小节的顺序相同,最高优先级在前。因此,例如,允许作为二进制 + 运算符 (6.5.6) 的操作数的表达式是 6.5.1 到 6.5.6 中定义的那些表达式。例外情况是作为一元运算符 (6.5.3) 的操作数的强制转换表达式 (6.5.4),以及包含在以下任何一对运算符之间的操作数:分组括号 () (6.5.1)、下标括号 [] (6.5 .2.1)、函数调用括号 () (6.5.2.2) 和条件运算符 ? : (6.5.15)。在每个主要子条款中,运算符具有相同的优先级。左或右结合性在每个子条款中由其中讨论的表达式的语法指示。

86) 在程序执行期间多次计算的表达式中,其子表达式的未排序和不确定排序的计算不需要在不同的计算中一致地执行。

i++相对于较大表达式的副作用的应用顺序i++ + i指定;这两个操作相对于彼此是无序的。如果在求值后立即应用副作用i++,那么你将得到 1 + 2 的结果。如果副作用推迟到相加之后,你将得到 1 + 1 的结果。

旧版本的标准更清晰一些。通过对序列点之间的表达式求值,对象最多可以修改其值一次,并且表达式的先前值仅用于确定要存储的新值。

该行为未定义,因此编译器不需要检测这些问题并发出诊断(i++ + i很容易捕获,但这个问题有更微妙的变化,更难检测)。就语言定义而言,任一结果都是“正确的”。

于 2013-02-20T14:41:49.767 回答
-3

在 C 中,后递增/递减将在当前操作完成后执行。在这个例子中,i++ + i;i 将在添加完成后递增。

于 2013-02-20T13:48:05.247 回答