Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.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# 为什么结构存储在堆栈上,而类存储在堆(.NET)上?_C#_.net - Fatal编程技术网

C# 为什么结构存储在堆栈上,而类存储在堆(.NET)上?

C# 为什么结构存储在堆栈上,而类存储在堆(.NET)上?,c#,.net,C#,.net,我知道类和结构之间的区别之一是结构实例存储在堆栈上,类实例(对象)存储在堆上 因为类和结构非常相似。有人知道这种特殊区别的区别吗?实际上,能够为某些目的在堆栈上分配内存是很有用的,因为这些分配非常快 然而,值得注意的是,并不能从根本上保证所有结构都放在堆栈上。Eric Lippert最近写了一篇关于这个主题的文章。我认为是否使用堆栈或堆空间是两者之间的主要区别,也许这篇文章会对您的问题有所帮助:每个进程都有一个由两个不同的可分配内存段组成的数据块。这些是堆栈和堆。堆栈主要用作程序流管理器,并保存

我知道类和结构之间的区别之一是结构实例存储在堆栈上,类实例(对象)存储在堆上


因为类和结构非常相似。有人知道这种特殊区别的区别吗?

实际上,能够为某些目的在堆栈上分配内存是很有用的,因为这些分配非常快


然而,值得注意的是,并不能从根本上保证所有结构都放在堆栈上。Eric Lippert最近写了一篇关于这个主题的文章。

我认为是否使用堆栈或堆空间是两者之间的主要区别,也许这篇文章会对您的问题有所帮助:

每个进程都有一个由两个不同的可分配内存段组成的数据块。这些是堆栈和堆。堆栈主要用作程序流管理器,并保存本地变量、参数和返回指针(在从当前工作函数返回的情况下)

与struct(或基本类型——ints、chars等)等值类型相比,类非常复杂,而且大多是非常大的类型。因为堆栈分配应该专门考虑程序流的效率,所以它不能为保存大型对象提供最佳环境


因此,为了满足这两种期望,出现了这种独立的体系结构。

值类型放在堆栈上,引用类型放在堆上。结构是一种值类型

不过,规范中对此没有任何保证,因此在未来的版本中可能会有所更改:)

(编辑以涵盖注释中的要点)

需要强调的是:值类型和引用类型之间既有差异也有相似之处,但这些差异与堆栈和堆无关,与复制语义和引用语义有关。特别是,如果我们这样做:

Foo first = new Foo { Bar = 123 };
Foo second = first;
那么“第一”和“第二”指的是同一份
Foo
?还是不同的副本?恰好,堆栈是一种将值类型作为变量处理的方便而有效的方法。但这是一个实现细节

(结束编辑)

所有的“值类型都在堆栈上”的事情…-值类型不总是在堆栈上

  • 如果它们是类上的字段
  • 如果它们是盒装的
  • 如果它们是“捕获的变量”
  • 如果它们在迭代器块中
然后它们进入堆(最后两个实际上只是第一个的奇特示例)

i、 e

class-Foo{
int i;//在堆上
}
静态void Foo(){
int i=0;//由于捕获而在堆上
// ...
Action act=委托{Console.WriteLine(i);};
}
静态IEnumerable Foo(){
int i=0;//在堆上执行迭代器块
//
收益率i;
}

另外,Eric Lippert(如已经指出)在这个主题中有一个关于

的一些语言,如C++,对象也是值类型。p> 要找到相反的例子比较困难,但在经典的Pascal union结构下,只能在堆上实例化。(正常结构可能是静态的)

简言之:这种情况是一种选择,而不是一条硬性法律。由于C#(以及之前的Java)缺乏过程基础,人们可以自问为什么它需要结构


它存在的原因可能是外部接口需要它,并且需要一个性能良好且紧凑的复杂(容器)类型。比课堂快的人。然后最好将其设置为值类型。

编译器和运行时环境处理内存管理的方式已经发展了很长一段时间。堆栈内存vs.堆内存分配决策与编译时已知的内容和运行时已知的内容有很大关系。这是在托管运行时之前

一般来说,编译器对堆栈上的内容有很好的控制,它可以根据调用约定决定清理什么以及何时清理。另一方面,这堆东西更像是荒野的西部。编译器无法很好地控制事情的来去。通过在堆栈上放置函数参数,编译器能够生成一个作用域——该作用域可以在调用的整个生命周期内进行控制。这是放置值类型的自然位置,因为它们易于控制,而引用类型可以将内存位置(指针)分发给任何想要的人

现代内存管理在很大程度上改变了这一点。NET运行时可以通过复杂的垃圾收集和内存管理算法控制引用类型和托管堆


我建议你看看编译器上的一些文章——我是在Aho上长大的,所以我。通过阅读,您还可以了解有关主题的很多信息。

主要区别在于,堆可能包含永久存在的对象,而堆栈上的某些内容是临时的,因为当封闭的调用站点退出时,它将消失。这是因为当一个人输入一个方法时,它会增长到包含局部变量以及调用方方法。当方法退出(ab)时,通常返回或由于异常,必须从堆栈中弹出每个帧。最终,感兴趣的框架被弹出,上面的所有东西都丢失了。

Marc Gravell已经奇妙地解释了值和引用类型如何复制的区别,这是它们之间的主要区别

至于为什么值类型通常在堆栈上创建,那是因为它们的复制方式允许这样做。堆栈在性能方面比堆有一些明显的优势,特别是因为编译器可以计算在特定代码块中创建的变量的确切位置,这使得访问速度更快

当您创建引用类型时,您将收到对中存在的实际对象的引用
class Foo {
    int i; // on the heap
}

static void Foo() {
    int i = 0; // on the heap due to capture
    // ...
    Action act = delegate {Console.WriteLine(i);};
}

static IEnumerable<int> Foo() {
    int i = 0; // on the heap to do iterator block
    //
    yield return i;
}