Stack - 为valuetypes存储的值在哪里?

时间:2011-10-07 07:12:01

标签: c# memory-management stack

void main()
{
    int x = 5; // stack-allocated
    Console.WriteLine(x);
}

我知道x是堆栈分配的。但实际存储在堆栈中的x是什么?它是否包含“实际值”,或者包含值的内存中的某个地址?

4 个答案:

答案 0 :(得分:8)

答案是响亮的“它取决于”。

有一点是肯定的:局部变量x的存储(如果存在的话)包含实际值而不是引用,因为int是值类型。

在简洁的脑translation翻译中,x的实际值(不是指针或对它的引用)存储在当前调用的激活记录中的局部变量中。生成的IL将是这样的:

  .maxstack 1
  .locals init (int32 V_0)

  ldc.i4.5  // push 5 on the evaluation stack
  stloc.0   // pop it into x

  ldloc.0   // now push a copy of x to pass to ...
  call void [mscorlib]System.Console::WriteLine(int32)

  ret       // return

(请注意,.maxstack指令正在讨论IL评估堆栈,不一定与本机堆栈相对应。评估堆栈(如果浅)通常由JIT编译的代码保存在寄存器中。)

在CLR的大多数当前实现中,激活记录及其局部变量将存储在本机堆栈中。所以,天真地,我认为x可以被认为是将其值存储在堆栈中。

在上述代码的优化编译(或优化JIT转换)中,IL中根本不存在局部变量存储。毕竟,为什么要分配局部变量存储来存储它,然后浪费两个使用该局部变量的指令,当你再也不需要它时?它可以作为WriteLine的常量参数内联:

  .maxstack 1
  // Just pass 5 to WriteLine.  No local variables here.
  ldc.i4.5
  call void [mscorlib]System.Console::WriteLine(int32)
  // Bye.
  ret

还有更多可能的并发症。如果函数在C#5 CTP中声明为async,或者函数中其他位置的lambda表达式捕获x,或者函数是使用yield return构建的枚举器声明,x的存储可能最终强制进入堆:它将成为编译器生成的类中的一个字段,这样如果闭包或延续转义本地,变量的存储可以在堆栈帧的拆除中存活下来范围。

答案 1 :(得分:0)

它存储x的实际值。没有地址或其他东西。

答案 2 :(得分:0)

我想这行已转换为ldind.i命令。规格说:

  

4D ldind.i将地址addr中的native int值作为native int加载到堆栈中。

答案 3 :(得分:0)

可能由于int的字节大小将小于或等于处理器的寄存器大小,因此很难预测CLR会选择这样做的情况。

然而,尽管这可能具有学术意义,但在使用C#编写程序时不需要考虑。它的实现细节,它可以改变,代码仍然可以工作。 CLR实施者使我们摆脱了这一责任。