不同的语言如何处理a = a ++?

时间:2014-09-28 03:52:09

标签: c

我开箱即用的Xcode C编译器和Chrome控制台(Javascript解释器)都无法递增,导致3。

来自我的C:

片段

int a = 3;
a = a++;
printf("%d\n",a);

我已经读过这样的C / C ++ [编译器]代码的问题与序列点之间的多个修改有关。这个概念是否适用于其他公共语言实现?

4 个答案:

答案 0 :(得分:2)

在C / C ++中

a = a++;
a = ++a  

两者都会调用未定义的行为。

C标准规定:

  

在上一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算修改一次。此外,只能访问先前值以确定要存储的值。

随着评估顺序的规则在不同语言中发生变化,行为也会发生变化 对于C#,order of evaluation rule表达式中每个操作数的计算顺序)是:

  

“评估每个子表达式严格从左到右”。

这意味着上述两个表达式都有效。在C#中,i++ = ++i始终保证为false++i = i++true。 Java也是如此。

在Python中没有++--运算符,所以不要考虑这个:)。

答案 1 :(得分:1)

每种语言都可以自由定义自己的规则。您提到了C,而在C中,i = i++是未定义的行为。这允许编译器编写者不支持该表达式,或决定如何翻译它。人们将运行他们的编译器,测试程序,然后根据他们的特定编译器发布答案。大多数编译器编写者更喜欢以某种理智的方式编译表达式,甚至可能记录他们的特定实现如何定义未定义行为(UB),但仅适用于该实现,甚至可能适用于该版本。使用归类为UB的功能的唯一方法是避免它们;语法实际上是不合法的,即使它编译。<​​/ p> 另一方面,C#选择定义行为,并以我认为基于堆栈的实现自然的方式处理它(当然还有其他关于自然和直观的观点,因此跨语言的差异)虽然没有说C#必须在CLR或基于堆栈的实现上运行。

我不会使用C#,而是使用与C#有一些相似之处的可乐.NET编译器,我选择使用相同的前缀和后缀增量规则。

如果你可以阅读基于堆栈的操作码,这就是我实现它的方式,据我所知,它在C#中完全相同。

假设:

int i = 3;

以下是以下行的编译方式:

i = i++;

i ++在增量之前求值为i,因为i ++是一个后增量运算。因此,我们将i的原始值存储到临时(在.NET CLR中,我们将其存储在堆栈中)。由于左手边的最终分配将在其他所有事件之后发生,并且我们将分配的值在增量开始之前被“冻结”,所以在增量过程中我发生了什么并不重要,我们将踩踏在最后。

ldloc 'i'   //  push pre-increment value (3) to be assigned to left side
ldloc 'i'   //  push pre-increment value (3) for the increment expression
ldc.i4.1    //  push constant 1, the amount to increment by
add         //  Do the increment, add top 2 operands, resulting in 4 on stack
stloc 'i'   //  pop top of stack (4) into i, now the increment is complete and i is 4
stloc 'i'   //  pop the top of stack (3), final assignment to i on left hand side

4的i是暂时存在的鬼值。

如果将左侧更改为k,则更简单,但i中的最终结果实际上会有所不同:

k = i++;

因为我们只分配给我一次,所以没有丢失或重影值。

ldloc 'i'
ldloc 'i'
ldc.i4.1
add
stloc 'i'   //  pop top of stack (4) into 'i', now the increment is complete and 'i' is 4
stloc 'k'   //  pop the top of stack (3) off and assigns to 'k' on left hand side

现在k == 3而i == 4

答案 2 :(得分:0)

虽然对于C / C ++不能说 (由于在同一序列点内进行修改),

此行为在某些语言(包括Java,C#和JavaScript)中得到了明确定义。 (确实应该有不同的问题/答案来证明这些案例。)

在这三种语言(Java / C#/ JS)中,postfix ++运算符立即应用于l值,产生原始,带有副作用保证在转让之前发生。这是保证对所有表达式进行严格的左右评估的结果。

我相信perl对这些运算符有一些怪癖(它可能会为变量产生引用),但在这种特殊情况下无法绘制出来。其他语言必须在逐案的基础上进行分析,因为它们是不同的语言,具有不同的规则和保证。

答案 3 :(得分:-2)

在增加前存储值时,它将返回3。使用前缀运算符而不是后缀。

int a = 3;
a = ++a;
printf("%d\n",a);

现在它会增加并打印4。