Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/256.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 通过引用传递值类型_C#_Parameters_Heap Memory_Value Type_Stack Memory - Fatal编程技术网

C# 通过引用传递值类型

C# 通过引用传递值类型,c#,parameters,heap-memory,value-type,stack-memory,C#,Parameters,Heap Memory,Value Type,Stack Memory,我正在学习引用类型、值类型、堆栈和堆以及它们之间的差异 现在我遇到了一件让我有点难以置信的事情。这里有一个代码示例来说明我的意思 void Main() { int foo = 0; PassFoo(ref foo); Console.WriteLine(foo); } void PassFoo(ref int bar) { bar = 1; } 现在,只要我们使用ref关键字,输出就会是1。如果我们删除ref关键字,那么输出将为0。我理解这是因为整数是值类型,当我们按值传递

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

现在我遇到了一件让我有点难以置信的事情。这里有一个代码示例来说明我的意思

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不应该因为它运行在不同的堆栈框架中而无法访问它吗


我意识到我可能不太了解这是如何工作的,因此非常感谢您的澄清。

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

你的理解很好。通过引用传递值类型将导致框架框住该参数,即在堆上创建的对象中包装对该参数的引用。我不知道你说的堆叠框架是什么意思。Main和PassFoo方法都可以访问同一个堆栈

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

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


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

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

这不是真的。我的理解是,我们不能直接访问foo,因为我们不知道c中的引用或c中的地址。想想我们用c语言编程,你可以访问任何内存。所以我们使用passbyreference,这样我们就可以通过引用访问foo的值。您可以将堆栈帧视为物理函数块,以便更轻松地操作内存和函数


这里有一篇关于

通过ref传递不需要装箱的文章。较高堆栈帧中的变量仍然可以在嵌套的方法调用中访问,因此获取它们的地址没有问题。当您试图在已返回的方法中返回指向本地的指针时,就会出现问题。对,我意识到我的理解可能不是100%准确。所以基本上你要说的是,当一个方法访问其他堆栈帧中的内存时没有实际的限制,但是编译器施加的限制,但是在这种情况下,由于我们使用ref关键字,编译器不会应用这些限制?@OverlyExcessive。通常无法访问另一个方法体的堆栈帧的原因是,本地方法在其方法体之外不可访问,因此它们是本地的,并且大多数其他抓住堆栈帧中某个对象地址的方法都是被禁止的,或者将局部变量提升到不在堆栈上的某个位置,即闭包,而不是允许引用堆栈逃逸方法体。