C++ 中的存储持续时间与位置

Storage duration vs location in C++

有时,我看到存储持续时间和发生这种情况之间存在混合概念。那是因为有时候我看到了下面的语句:

int i; // This is in the stack!
int* j = new int; // This is in the heap!

但这真的是 100% 正确的吗? C++ 是否确保 存储发生在 的位置?还是编译器决定的?

存储的位置与时长无关吗?

例如,以这两个片段为例:

void something()
{
   int i;
   std::cout << "i is " << i << std::endl;
}

对比:

void something()
{
   int* i = new int;
   std::cout << "i is " << i << std::endl;
   delete i;
}

关于 i 的生命周期,两者或多或少是等价的,它在块的开头创建并在块的末尾删除,这里编译器可以只使用堆栈(我不知道!),反之亦然:

void something()
{
   int n[100000000]; // Man this is big
}

对比:

void something()
{
  int* n = new int[100000000];
  delete n;
}

这两种情况应该在堆中以避免堆栈溢出(或者至少到目前为止我被告知...),除了存储持续时间之外,编译器是否也考虑到了这一点?

Is the location of the storage independent from the duration?

A0:持续时间指定 expected/required 行为。
A1:标准没有具体说明是如何实现的。
A2: 标准甚至不需要堆或栈!

void something()
{
   int i;
   std::cout << "i is " << i << std::endl;
}

void something()
{
   int* i = new int;
   std::cout << "i is " << i << std::endl;
   delete i;
}

在第一个示例中,您有 "automatic" 个存储持续时间,第二个示例是 "dynamic" 个存储持续时间。不同之处在于 "automatic" 将 总是 在作用域结束时被销毁,而第二个只会在 delete is executed.

时被销毁

创建对象的位置未由标准指定,完全留给实施。

关于使用底层堆的实现,这对于第一个示例来说是一个简单的实现选择;但不是必需的。该实现可以很容易地为整数所需的 space 调用动态内存的 OS 并且仍然表现得像标准定义一样,只要释放内存的代码也被植入并在对象时执行超出范围。

相反,实现动态存储持续时间(第二个示例)的简单方法是从运行时分配内存,然后在您点击删除时释放它(假设您的实现具有此能力)。但这不是必需的。如果编译器可以证明没有异常并且你总是会点击删除,那么它可以很容易地将它放在堆上并正常销毁它。 注意:如果编译器确定对象总是泄漏。它仍然可以将它放在堆上,并且在它超出作用域时不销毁它(这是一个完全有效的实现)。

The second set of examples adds some complications:

代码:

int n[100000000]; // Man this is big

这确实很大。某些实现可能无法在堆栈上支持此功能(堆栈帧大小可能受限于 OS 或硬件或编译器)。

一个完全有效的实现是为此动态分配内存,并确保在对象超出范围时释放内存。

另一种实现是简单地预先分配内存,而不是在堆栈上,而是在 bzz 中(从这里的内存开始。这是存储内存的应用程序的汇编程序区域)。只要它实现了在范围末尾调用任何析构函数的预期行为(我知道 int 没有析构函数,所以这很容易)。

Does C++ ensure where the storage takes place? Or it is decided by the compiler?

当你声明一个变量时:

int i;

它有自动存储。它确实可以在堆栈上,但如果有足够的寄存器可用,通常只为它分配一个寄存器。理论上编译器为这个变量分配堆内存也是有效的,但实际上并没有发生。

当你使用new时,实际上是由标准库为你分配内存。默认情况下,它将使用堆。但是,理论上它也可以在堆栈上分配内存,但当然这通常是错误的做法,因为当您从调用 new 的函数中 return 时,任何堆栈存储都会消失.

其实new+一样只是一个运算符,你可以重载它。通常,您会在 class 中重载它,但您也可以重载全局 new 运算符(以及类似的 delete 运算符),并让它从您想要的任何地方分配存储。

Is the location of the storage independent from the duration?

原则上是的,但实际上只有函数持续时间的自动变量被放置在堆栈上,而您使用 new 分配的数据通常旨在比调用的函数更长寿它,然后继续堆。

Those two cases should be in the heap to avoid stack-overflow (Or at least is what I've been told so far...), does the compiler that also that into account, besides the storage duration?

据我所知,GCC 和 Clang 从未对具有自动存储的变量使用堆分配,无论它们的大小如何。因此,您必须自己使用 newdelete,或者使用为您管理存储的容器。某些容器,如 std::string,如果您只在其中存储少量元素,将避免堆分配。