2014-04-11 156 views
0

我正在学习有关引用类型,值类型,堆栈和堆以及它们之间差异的过程。通过引用传递值类型

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

void Main() 
{ 
    int foo = 0; 
    PassFoo(ref foo); 
    Console.WriteLine(foo); 
} 
void PassFoo(ref int bar) 
{ 
    bar = 1; 
} 

目前的情况是,现在的产量将1只要我们使用裁判关键字。如果我们删除ref关键字,则输出将为0。我明白,这是因为整数值类型,当我们按值传递foo我们正在复制由位取到bar但是当我们添加裁判关键字我们,而不是只通过的foo内存ADRESS在这个例子中,这也是我们改变foo的原因的原因。我到目前为止是否正确?

...现在来混淆我的部分。我对堆栈工作原理的理解是,它只能访问当前正在运行的堆栈帧。这就是为什么PassFoo无法直接访问foo。我还了解到值类型存储在声明的位置。所以当我们通过foo来引用时,我们将foo的内存地址传递给bar对不对?但PassFoo()不应该不可访问,因为它在不同的堆栈框架中运行?

我意识到我可能不完全了解这是如何工作的,所以澄清将不胜感激。

+2

变量仍处于嵌套的方法调用访问的,所以有与服用他们的地址没有问题。当您尝试在返回的方法中返回指向本地的指针时,会发生问题。 – Lee

回答

2

我对堆栈工作原理的理解是,它只能访问当前正在运行的堆栈帧。

这是不正确的。在引擎盖下,一种方法能够从任何堆栈帧访问存储器。 C#编译器只是简单地应用约束条件,在大多数情况下,对栈上位置的引用不会暴露在方法体外。这种情况下,使用ref关键字就是这种情况的一个例外。一旦达到较低的抽象层次,即编译器生成的IL代码,就没有任何约束可以禁止从另一个方法的主体访问堆栈。

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

+0

对,我意识到我的理解可能不是100%准确的。所以基本上你说的是,当涉及到访问其他堆栈帧中的内存的方法时,没有实际的限制,但是编译器施加的限制,但是在这种情况下,因为我们使用* ref *关键字,所以编译器赢得了不适用这些限制? –

+2

@OverlyExcessive非常多。通常不能访问另一个方法体的堆栈框架的原因是因为本地方法不能在其方法体外访问(因此为什么它们是本地的),以及大多数其他方式来获取某个地址在堆栈框架中被禁止,或者将局部变量提升到不在堆栈上的东西(即关闭),而不是允许引用堆栈以逃避方法体。 – Servy

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; 
} 
+0

Downvoter谨慎解释? – reggaeguitar

-2

您的理解相当不错。按引用传递值类型将导致框架将该参数框起来,即将引用包装在堆上创建的对象中。我不确定你的意思是“堆栈帧”。两种方法(“Main”和“PassFoo”)都可以访问相同的堆栈。

+1

通过ref传球并不需要拳击。 – Lee

0

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

0

这不是真的。我的理解是我们无法直接访问foo,因为我们不知道参考文献(在c#中)或地址(在c中)。想想我们在c编程,你可以访问任何内存。所以我们使用引用传递,比我们可以通过引用访问foo的值。您可以将堆栈框架视为物理功能块,以更轻松地操纵内存和功能。

这里的一篇文章中更高的堆栈帧约stack.