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

ebb*_*ebb 3 c# stack memory-management

void main()
{
    int x = 5; // stack-allocated
    Console.WriteLine(x);
}
Run Code Online (Sandbox Code Playgroud)

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

Jef*_*tin 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
Run Code Online (Sandbox Code Playgroud)

(注意,该.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
Run Code Online (Sandbox Code Playgroud)

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