通过引用传递值类型
本文关键字:类型 引用 | 更新日期: 2023-09-27 17:57:52
我正在学习引用类型、值类型、堆栈和堆以及它们之间的差异。
现在我遇到了一些让我有点难以置信的事情
void Main()
{
int foo = 0;
PassFoo(ref foo);
Console.WriteLine(foo);
}
void PassFoo(ref int bar)
{
bar = 1;
}
现在,只要我们使用ref关键字,输出就会是1
。如果我们删除ref关键字,那么输出将是0
。我理解这是因为整数是值类型,当我们按值传递foo
时,我们将逐位将值复制到bar
,但当我们添加ref关键字时,我们只传递堆栈上foo
的内存地址,这也是我们在本例中更改foo
值的原因。到目前为止我是对的吗?
现在来谈谈让我困惑的部分。我对堆栈工作原理的理解是,它只能访问当前运行的堆栈帧。这就是PassFoo
不能直接访问foo
的原因。我还了解到值类型存储在声明它们的位置。这就是让我困惑的地方,当我们通过引用传递foo
时,我们将foo
的内存地址传递给bar
,对吗?但是PassFoo()
不应该因为在不同的堆栈帧中运行而无法访问它吗?
我意识到我可能不太了解这是如何工作的,所以如果能澄清一下,我将不胜感激。
我对堆栈工作方式的理解是,它只能访问当前运行的堆栈帧。
这不是真的。在引擎盖下,一种方法能够从任何堆栈帧访问内存。C#编译器只是应用约束,这样在大多数情况下,对堆栈上位置的引用就不会暴露在该方法体之外。这种情况,ref
关键字的使用,是这种情况的一个例外。一旦你进入了较低的抽象层,即编译器生成的IL代码,就没有任何约束可以禁止从另一个方法的主体访问堆栈。
你问题的前半部分是关于发生了什么的有效解释。
当您将foo传递到PassFoo时,您正在传递foo所在的地址。由于PassFoo知道foo的值存储在哪里,因此它可以更改该内存地址中的值。PassFoo的堆栈帧仅包含变量的地址。以下是具有指针的等效C代码
#include <stdio.h>
void PassFoo(int* bar)
{
*bar = 1;
}
int main()
{
int foo = 0;
PassFoo(&foo);
printf("%d", foo);
return 0;
}
如果该方法调用另一个方法,则新方法会在堆栈顶部创建其堆栈框架。通过这种方式,每个新方法都可以在分配给堆栈的内存中分配自己的局部变量,堆栈还用于存储参数和方法之间传递的返回值,这就是为什么存储在bar参数中的指向foo的指针是可见的。
事实并非如此。我的理解是,我们不能直接访问foo
,因为我们不知道引用(在c#中)或地址。想想我们是用c编程的,你可以访问任何内存。因此,我们使用引用传递,而不是通过引用访问foo的值。您可以将堆栈框架视为物理函数块,以便更容易地操作内存和函数。
这是一篇关于stack的文章。
你的理解力很好。通过引用传递值类型将导致框架框住该参数,即将对该参数的引用包装在堆上创建的对象中。我不知道你说的"堆叠框架"是什么意思。两个方法("Main"answers"PassFoo")都可以访问同一个堆栈。