【问题标题】:What does the C++ standard guarantee regarding the storage, allocation of local variables?C++ 标准对局部变量的存储、分配有什么保证?
【发布时间】:2018-05-21 11:05:08
【问题描述】:

让我们考虑一下这个简单的 C++ 代码:

struct vector3d { double x, y, z; };

void foo()
{
    vector3d v;
    ...
}

C++ 对v 的位置有何看法?我的猜测是:什么都没有,这是有道理的,因为 C++ 不应该为这样的底层内存概念而烦恼。

但是如果 C++ 标准没有讨论堆栈、堆以及动态内存分配的具体概念,它如何保证在这种情况下编译器不会选择通过对malloc 的底层调用来翻译这一行而不是经典的sub rsp, X

如果它不保证任何东西,C 是否保证某些东西(出于好奇)?是不是所有 C++ 开发人员都普遍认为这将在堆栈上?

谢谢!

【问题讨论】:

  • C++ 中的变量存储在堆栈中...如果您想留在堆上,可以使用new 运算符或unique_ptrshared_ptr... 但最终它们使用@ 987654328@
  • “它如何保证在这种情况下编译器不会选择通过底层调用 malloc 而不是经典的 sub rsp, X 来翻译这一行” - 它没有。这正是我们可能会在 C++20 中看到协程的原因。
  • @elvis.dukaj "C++ 中的变量存储在堆栈中" 如问题所述 - C++ 标准甚至没有提到 stackheap。因此,没有什么可以禁止人们编写一个不在堆栈上存储变量并且仍然符合标准的编译器。
  • 如果foo() 从未被递归调用(或多线程),则可以在固定地址分配局部变量。
  • @AntiClimacus - 当协程将控制权交给调用代码时,必须保留协程的状态(其范围内所有局部变量的值)。调用代码可能会在它之后调用一个常规函数,而一旦我们扩大规模,它就不能很好地与堆栈配合使用。所以更简单的实现是在空闲存储或堆上分配状态。由于该标准不强制“调用堆栈”,因此不需要调整其范围规则以支持协程(当然它仍然需要在其他地方进行调整)。

标签: c++ memory standards


【解决方案1】:

你说得对,C++ 标准没有直接说明这一点。事实上,从标准的 POV 来看,诸如“堆栈”或“堆”之类的概念是没有意义的。

这实际上是一个实施质量问题,除了假设的 Hell++(其座右铭是“坚持 RAW 并尽可能违反 RAI”),没有编译器有理由存储自动持续时间的局部变量在堆上。

【讨论】:

  • 该标准在描述异常处理时提到了术语“堆栈”,并详细描述了描述对象如何以与其构造相反的顺序被破坏的概念 - 堆栈展开。跨度>
  • “创建一个函数范围的自动变量没有合法的失败方式” 是不是说栈溢出导致的崩溃不符合?
  • @HolyBlackCat 哎呀,我的措辞似乎有点太强烈了。我会想一个更好的方式来表达我的意思。
  • 我在哪里可以接触到 Hell++?
  • @AntiClimacus RAW - Rules As Written。 RAI - Rules As Intended.
【解决方案2】:

事实上,本地对象(自动对象)必须以与其创建相反的顺序销毁。因此,堆栈的概念是标准期望局部变量的行为方式所固有的。在描述对象销毁时,它还提到了术语“堆栈展开”。

术语“堆栈”作为通用计算术语因此很好地应用于C++ 局部变量。这种“堆栈”是如何实现的,以及它可能位于内存中的哪个位置,都值得一提,但堆栈基本上是一个概念,即后进先出。这就是 C++ 标准所说的必须进行本地对象创建/销毁的方式。

“堆”这个术语虽然没有在标准中使用,但它是一个通用计算术语,它完全适用于 C++ 标准的“免费存储”所做的事情。这是一个可以获取和返回任意存储块的一般区域。

因此标准确实规定 C++ 程序需要(至少)这两种类型的存储。一种行为类似于堆栈(以相反的创建顺序销毁),另一种行为类似于堆(任意分配/解除分配)。

【讨论】:

  • 该标准还允许将局部变量存储在处理器寄存器中。局部变量并不总是放在堆栈中。
  • @ThomasMatthews 我的观点是“堆栈”是一个概念,它决定了对象的创建和销毁顺序。实际存储它们的在哪里 并不重要,当最后创建的第一个被销毁时,它仍然是一个堆栈。该概念体现在标准中。所以实现细节是无关紧要的。
  • @ThomasMatthews 该标准在异常期间仍然使用术语“堆栈展开”,即使 展开堆栈 上的 一些 对象可能只存在在 CPU 寄存器中。
  • @ThomasMatthews 事实上,该标准根本没有提到 CPU 寄存器。它们是允许的,因为编译器只需要表现得就好像遵守了标准。所以标准描述了 stack like 行为,但是编译器可以实现它喜欢的方式只要它的行为像一个堆栈。
猜你喜欢
  • 2012-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多