void main()
{
int x = 5; // stack-allocated
Console.WriteLine(x);
}
我知道x
是栈上分配的。但实际上,关于x
在堆栈中存储了什么?它保存的是“真实值”还是指向内存中包含该值的某个位置的地址?
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 评估堆栈,不一定对应本机堆栈。如果浅层次的评估堆栈通常会被 JIT 编译代码保存在寄存器中。x
的值可能被认为是存储在堆栈上。WriteLine
中。 .maxstack 1
// Just pass 5 to WriteLine. No local variables here.
ldc.i4.5
call void [mscorlib]System.Console::WriteLine(int32)
// Bye.
ret
可能还存在更多的问题。如果该函数声明为async
(如C# 5 CTP中),或者x
在函数的其他位置被lambda表达式捕获,或者该函数是使用yield return
语句构建的枚举器,那么x
的存储可能最终被强制转移到堆上:它将成为编译器生成类中的一个字段,以便变量的存储可以在闭包或继续逃逸本地范围的情况下生存堆栈帧的拆卸。
它存储了一个实际值x。没有地址或其他东西。
我认为这行代码被翻译为ldind.i
命令。规范说明如下:
4D
ldind.i
将地址addr处的本机int值作为本机int加载到堆栈上。
ldind.i
,因为这是在不安全的代码中用于取消引用 int *
的操作。 - Jeffrey Hantin很可能,由于int
的字节大小会小于或等于您处理器的寄存器大小,因此很难预测CLR会选择做出其他决策的情况。
但是,尽管这可能是学术上感兴趣的事情,但在使用C#编写程序时不需要考虑它。 这只是一个实现细节,它可能会改变,代码仍然可以工作。 CLR实现者已经解放了我们的责任。