通过引用传递值类型

本文关键字:类型 引用 | 更新日期: 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")都可以访问同一个堆栈。