void main()
{
int x = 5; // stack-allocated
Console.WriteLine(x);
}
我知道 x
是堆栈分配的。但是关于 x
的堆栈中实际上存储了什么?它保存的是“实际值”,还是包含该值的内存中某个位置的地址?
最佳答案
答案是响亮的“视情况而定”。
有一件事是肯定的:局部变量 x
的存储,如果它存在的话,包含一个实际值而不是一个引用,因为 int
是一个值类型。
在简洁的脑死亡翻译中,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 评估堆栈,不一定对应于 native 堆栈。如果评估堆栈较浅,则通常由 JIT 编译代码保存在寄存器中.)
在 CLR 的大多数当前实现中,激活记录及其局部变量将存储在 native 堆栈中。所以,我天真地认为 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
,或者如果 x
被函数中其他地方的 lambda 表达式捕获,或者如果函数是使用yield return
语句,x
的存储最终可能会被强制放到堆上:它将成为编译器生成的类中的一个字段,这样变量的存储就可以在撕裂后继续存在- 如果闭包或延续超出局部作用域,则栈帧向下。
关于c# - 堆栈 - 值类型的值存储在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7684108/