【问题标题】:How does compiler handle return statement with postfix operator?编译器如何处理带有后缀运算符的返回语句?
【发布时间】:2013-10-01 22:47:18
【问题描述】:

有人能解释一下下面的代码是如何工作的吗?

static int index = 0;
public static int GetNextIndex()
{
    return index++;
}

我假设,由于递增操作发生在 return 语句之后,变量“index”永远不会递增。

但在使用 C# 编译器进行测试时,我观察到“索引”正在增加。

标准编译器如何处理这种情况?

【问题讨论】:

  • 是什么让您认为增量发生在 return 语句之后?在获取到 index 的值后进行递增,供在return语句中使用...
  • 因为是后缀运算符。
  • @Fadi 阅读 Eric Lippert 的这个答案,解释前/后缀 stackoverflow.com/a/3346729/860585 的机制
  • 我通过调用函数进行了测试。在初始调用期间,输出为 0。这让我认为,增量操作发生在 return 语句之后。
  • 看看@Rotem 提供的链接(快几秒^^)。这是关于++确切前缀/后缀版本如何工作的非常深度答案。

标签: c# compiler-construction


【解决方案1】:

这是编译器生成的中间语言(IL)(VS2013RC/.NET 4.5.1RC):

.method public hidebysig static int32 GetNextIndex() cil managed
{
    .maxstack 8
    L_0000: ldsfld int32 ConsoleApplication4.Program::index
    L_0005: dup 
    L_0006: ldc.i4.1 
    L_0007: add 
    L_0008: stsfld int32 ConsoleApplication4.Program::index
    L_000d: ret 
}

那么,那有什么作用呢?假设index 在调用它之前的值为 6。

    L_0000: ldsfld int32 ConsoleApplication4.Program::index

index 的值加载到评估堆栈中 - 堆栈包含6

    L_0005: dup

复制堆栈顶部的值 - 堆栈包含6, 6

    L_0006: ldc.i4.1

将值 1 加载到堆栈中 - 堆栈包含 6, 6, 1

    L_0007: add 

将栈顶的两个值相加,并将结果放回栈中。堆栈包含6, 7

    L_0008: stsfld int32 ConsoleApplication4.Program::index

将栈顶值存储到indexindex 现在等于 7,堆栈包含 6

    L_000d: ret 

取栈顶(6)作为返回值。

【讨论】:

    【解决方案2】:
    static int index = 0;
    public static int GetNextIndex()
    {
        return index++;
    }
    

    相当于:

    static int index = 0;
    public static int GetNextIndex()
    {
        int i = index;
        index = index + 1;
        return i;
    }
    

    因此index 递增。

    【讨论】:

    • 嗯,基本上是等价的。从技术上讲,第一个代码 sn-p 仅在整个方法中评估表达式 index once ;你评估它三遍,但它非常接近。
    • 如果 index 不是 int 字段,这将是不等价的。评估 int 字段没有副作用。
    • 这使它成为三种情况之一的非问题,即分配给它的情况,因为它没有副作用。但是,您正在读取两次 index 的值,并且它可能在这两次读取之间发生了变化(来自另一个线程中的操作)。第一个代码 sn-p 不会从它读取两次,因此它不会观察到这样的变化。当然,如果计算表达式会产生副作用,它会让整个事情变得更加不同。
    • “线程安全”不是一个描述性术语。也许你的意思是说它不是原子的,这是真的,而且更精确。我没有说它原子的。我说过,变量只在执行运算符时读取一次。但是,因为它既可以读取也可以写入,所以整个操作不是原子的。这并没有改变这个代码从它读取两次而操作员从它读取一次的事实。两者都不是原子的,但它们也不相同。
    • 第二行不应该是index = i + 1吗?我认为这将纠正@Servy 提出的问题。
    猜你喜欢
    • 1970-01-01
    • 2021-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-17
    • 1970-01-01
    • 2013-02-16
    相关资源
    最近更新 更多