通过引用传递值类型

时间:2014-04-11 14:54:17

标签: c# parameters stack heap value-type

我正在学习引用类型,值类型,堆栈和堆以及它们之间的差异。

现在我遇到了一些令我难以置信的东西。这是一个代码示例来说明我的意思

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()不应该无法访问它,因为它在不同的堆栈帧中运行?

我意识到我可能对它的运作方式没有正确的理解,因此非常感谢澄清。

5 个答案:

答案 0 :(得分:2)

  

我对堆栈如何工作的理解是它只能访问当前运行的堆栈帧。

那不是真的。在引擎盖下,一种方法能够从任何堆栈帧访问内存。 C#编译器只是应用约束,这样在大多数情况下,对堆栈中位置的引用不会暴露在该方法的主体之外。在这种情况下,使用ref关键字是这种情况的一个例外。一旦你进入较低的抽象层,即编译器生成的IL代码,就没有任何禁止从另一个方法体中访问堆栈的约束。

你问题的前半部分是关于发生了什么的有效解释。

答案 1 :(得分:0)

当你将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;
}

答案 2 :(得分:0)

如果该方法调用另一个方法,则new方法在堆栈顶部创建其堆栈帧。这样每个新方法都可以在自己的分配给堆栈的内存部分中分配自己的局部变量,而堆栈也用于存储参数和返回值在方法之间传递,这就是为什么它指向foo的指针存储在bar参数可见。

答案 3 :(得分:0)

这不是真的。我的理解是,我们无法直接访问foo,因为我们不知道引用(在c#中)或地址(在c中)。想想我们在c编程,你可以访问任何内存。所以我们使用pass by reference,而不是通过引用访问foo的值。您可以将堆栈帧视为物理功能块,以便更轻松地操作内存和功能。

以下是关于stack.

的文章

答案 4 :(得分:-2)

你的理解非常好。通过引用传递值类型将导致框架对该参数进行包装,即在堆上创建的对象中包含对它的引用。我不确定你的意思是&#34;堆叠框架&#34;。两种方法(&#34; Main&#34;和#34; PassFoo&#34;)都可以访问同一个堆栈。