.NET - 存储在地址空间中的堆或堆栈上的函数变量?

Posted

技术标签:

【中文标题】.NET - 存储在地址空间中的堆或堆栈上的函数变量?【英文标题】:.NET - Function variables stored on heap or stack in address space? 【发布时间】:2014-02-28 23:50:14 【问题描述】:

我在考试中遇到了以下问题:

当一个程序调用一个函数时,属于哪种类型的数据结构 为该函数中的变量分配的内存?

    堆 队列 后进先出 堆栈

根据测试,HEAP 是正确答案,虽然我选择了 STACK。

可以请一位很棒的人解释原因吗?

提前致谢。

【问题讨论】:

The Stack Is An Implementation Detail, Part One 和 The Stack Is An Implementation Detail, Part Two 作者:Eric Lippert 假设的答案是错误的,基本上 - 在大多数情况下。虽然你通常不应该关心......根据埃里克的评论:) 有趣的帖子:***.com/a/14023708/172769 不幸的是,创建该考试的人似乎不太了解该主题 - 从他们问题的措辞和他们知道正确答案的信心来看(尤其是在像 C# 这样极其复杂的语言中) )。无法区分变量和对象的讲师可能不是 C# 或类似编程语言的好讲师。 @DawnFreeze,正如目前所描述的,这是一个糟糕的问题,但它确实让我想知道这里没有提供的考试问题是否有额外的背景。 【参考方案1】:

好吧,局部变量和参数存储在堆栈而不是堆上。对于本地值类型,这意味着值本身存储在堆栈中。对于本地引用类型,只有引用会在堆栈中。

为了获得更深入的解释,我建议阅读 Erik Lippert 的一篇非常好的博文(他已经在评论中指出了这篇博文):The Stack Is An Implementation Detail, Part One。

【讨论】:

视情况而定。捕获的本地存储在堆上。 即使它们在概念上存储在堆栈中,从 IL 代码的角度来看,运行时可能最终只会将值存储在寄存器中,而无需将其存储在内存中 曾经. 然后加入装箱和拆箱,这真是一团糟。 :) @DavidHaney Boxing 对 变量 的内存存储位置没有影响。它可能会影响类型的给定实例的存储位置,这可能是也可能不是该变量的值,或者是该变量包含引用的引用,这不是问题所要问的。跨度> @DavidHaney 好吧,它可能不在堆栈上,但如果不在,那是因为变量被封闭,或者在迭代器块中,等等。变量是装箱值的事实类型,或引用类型,或值类型,对变量的内存存储位置没有影响,只有变量“代表”的对象可能存储在哪里。【参考方案2】:

这是一个写得很糟糕的问题,我想知道写它的人的能力。正如 bejger 回答的那样,在大多数语言中,局部变量(和函数参数)都存储在“堆栈”中。在 C# 或 Java 等引用语言中,对象存储在“堆”中,并引用存储在“堆栈”中的对象(指针)。这个问题是可疑的,因为它没有指定语言或确切的场景。此外,我根本不会将堆和堆栈称为“数据结构”。它们是内存分配方案,而不是本文中的数据结构。

【讨论】:

我认为它们实际上是数据结构......堆栈可以展开 AKA 推送/弹出(想想:调用堆栈),而堆是由指针访问的随机放置的内存。 @dvnrrs 我认为您本可以发表评论而不对 OP 发表讽刺评论。 @DavidHaney 抱歉,被自动完成功能咬住了...纠正了我的错误。 @mrlucmorin 他不是在侮辱 OP,而是在批评 OP 引用问题的作者。 @mrlucmorin 我根本没有对 OP 发表讽刺评论。我正在对编写 OP 感到困惑的考试问题的人发表评论。这并不是要讽刺,但我确实觉得这个问题写得非常糟糕,这表明作者可能不是最好的导师。 (当然,每个人都会犯错。)【参考方案3】:

首先,C# 没有“函数”;它有“方法”。

“在哪种类型的数据结构中为该函数中的变量分配的内存是什么意思?”

Nota Bene: 仅作记录,“LIFO”是一种访问策略(后进先出), 不是数据结构。通常,人们将堆栈称为 LIFO 堆栈。但我离题了。

正确的答案通常是,要么

“视情况而定”,或 “堆栈和堆”

局部变量(仅存在于方法调用上下文中的变量)的插槽在方法调用期间分配在堆栈帧内,该堆栈帧位于程序堆栈中。

如果变量是引用类型,则该槽是对实际对象实例的引用,当/如果是,将从堆中为其分配内存实例化。

如果变量是值类型,则该槽[通常,但不总是]是对象实例本身。 . .但这不是给定的。如果需要,值类型可以(并且是)在堆上分配。在这种情况下,变量的堆栈帧槽就像值类型一样,是对在堆上分配的实例的引用。

【讨论】:

如果变量是值类型,那么“槽”总是是对象本身。这就是值类型的point。这是它的定义,该类型的变量始终包含实际对象,而对于引用类型,该类型的变量始终包含对存储在“其他地方”的对象的引用。值类型的变量是否存储在堆栈、堆或其他地方,完全是另一回事,并且肯定会有所不同,但无论为该变量分配的“槽”在哪里,确切地 对象的值存储在哪里,否则它不是值类型。 另外,“堆栈和堆”也不完全正确。除了这两个之外,还有其他选项,例如正在注册的变量。 这并不完全正确(忽略分配给 CPU 寄存器的局部变量等)。考虑object o = (object)3;。值类型是装箱的并且存在于堆上。或int[] foo = new int[] ... ;。那是 100 个整数,没有装箱,只存在于堆中。就像我说的,“这取决于”。但更重要的是,你不应该在意。 这两个变量都是引用类型的变量。它们不是值类型的变量。事实上,变量不包含对象本身,而是对存在于别处的对象的引用,这正是为什么根据定义,这些变量是“引用类型”而不是“值类型”的原因。 object 类型的变量始终是引用类型。数组总是引用类型。 那么 integer(s) 包含在哪里?

以上是关于.NET - 存储在地址空间中的堆或堆栈上的函数变量?的主要内容,如果未能解决你的问题,请参考以下文章

数据结构之堆栈

浅谈堆栈队列

数据结构&算法_堆栈(堆栈)队列链表

为啥.net(仍然)需要拳击?

Android 面试

字符串类型是存储在堆上还是栈上?