【问题标题】:Why is it impossible to allocate an array of an arbitrary size on the stack?为什么不能在堆栈上分配任意大小的数组?
【发布时间】:2017-02-19 17:04:53
【问题描述】:

为什么我不能写以下内容?

char acBuf[nSize];

只是为了防止堆栈过度增长? 或者是否有可能做类似的事情,如果我能确保我总是只占用几百 KB?

据我所知,std::string 使用其成员的内存来存储分配的字符串,只要它们不超过 15 个字符。只有当字符串较长时,它才会使用这块内存来存储一些堆分配内存的地址,然后再获取数据。

似乎必须在编译时 100% 确定堆栈在运行时将如何对齐。真的吗?这是为什么呢?

【问题讨论】:

  • 是的,这是真的。 C++ 不支持 VLA。
  • 如果您要分配给堆栈,(理论上)每个堆栈帧的所有内存都在其范围的开头分配,因此我们有动态存储,其中内存使用分配免费商店。

标签: c++ memory heap-memory dynamic-memory-allocation stack-memory


【解决方案1】:

它与防止堆栈溢出无关,您可以使用char a[SOME_LARGE_CONSTANT] 很好地溢出堆栈。在 C++ 中,数组大小必须在编译时知道,这是计算包含数组的结构的大小所需要的。

另一方面,自 C99 以来,C 就具有可变长度数组,这增加了一个异常并允许函数范围内的数组的运行时依赖大小。至于为什么 C++ 没有这个?它从未被 C++ 标准采用。

【讨论】:

  • 但是当我遵循这个:stackoverflow.com/a/5770919/1594594 我仍然无法使用 VLA 的 :(
  • @Allgaeuer Microsoft Visual Studio 的 C 实现不支持 VLA。
  • @Allgaeuer 您的链接提到了 ANSI C,即 C90。 Microsoft 从未提供完整的 C99 实现,我认为它们仅支持 C++ 所需的子集。该答案中的最后一个链接还提到不支持 VLA。
【解决方案2】:

为什么我不能写以下内容?

char acBuf[nSize];

这些称为可变长度数组 (VLA),不受 C++ 支持。原因是堆栈非常快,但与免费存储(用你的话来说就是堆)相比很小。这意味着在任何时候向 VLA 添加大量元素时,您的堆栈都可能会溢出,并且您会得到一个模糊的运行时异常。这也可能发生在编译时大小的堆栈数组中,但这些更容易捕获,因为程序的行为不会影响它们的大小。这意味着 x 不必在 y 之后发生以创建堆栈溢出,它就在那里。 This 更详细地报道了它。

像std::vector 这样的容器使用更大的免费存储,并且可以处理过度分配(抛出bad_alloc)。

【讨论】:

  • 我正准备将“detail and rage”更改为“detail and range” - 但是在阅读了链接的讨论之后,没有错字纠正。 VLA 肯定会引发激烈的讨论!
【解决方案3】:

与 C 不同,C++ 不支持可变长度数组。如果需要,可以使用非标准扩展,例如 alloca 或 GNU 扩展(由 clang 和 GCC 支持)。它们有它们的注意事项,因此请务必阅读手册以确保安全使用它们。

堆栈布局主要是静态确定的原因是,生成的代码必须执行更少的计算(加法、乘法和指针取消引用)来确定数据在堆栈上的位置。偏移量可以改为硬编码到生成的机器代码中。

【讨论】:

【解决方案4】:

我的建议是看看 alloca.h

   void *alloca(size_t size);

alloca() 函数在栈中分配 size 个字节的空间 调用者的框架。这个临时空间会自动释放 当调用 alloca() 的函数返回给它的调用者时。

【讨论】:

    【解决方案5】:

    我在 C++ 中看到的 VLA 的一个可能问题是类型。

    char acBuf[nSize] 中的acBuf 是什么类型,或者在char acBuf[nSize][nSize] 中更糟?

    template <typename T> void foo(const T&);
    
    void foo(int n)
    {
        char mat[n][n];
    
        foo(mat);
    }
    

    您不能通过引用来传递该数组

    template <typename T, std::size_t N>
    void foo_on_array(const T (&a)[N]);
    

    【讨论】:

      【解决方案6】:

      您应该很高兴 C++ 标准不鼓励危险的做法(堆栈上的可变长度数组),而是鼓励不太危险的做法(可变长度数组和带有堆分配的 std::vector)。

      栈上的变长数组更危险,因为:

      • 可用堆栈空间通常为 8 MB,远小于 2 GB(或更多)的可用堆空间。
      • 当堆栈空间耗尽时,程序会因 SIGSEGV 崩溃,需要特殊软件(例如 GNU libsigsegv)才能从这种情况中恢复。
      • 在典型的 C++ 程序中,程序员不知道数组长度是否一定会保持在 4 MB 等限制之下。

      【讨论】:

        【解决方案7】:

        为什么我不能写下面的? char acBuf[nSize];

        你不能这样做,因为在 C++ 中,数组的长度必须在编译时知道,这是因为编译器为数组保留了指定的内存,并且在运行时不能修改它。这不是关于防止堆栈溢出,而是关于内存布局。

        如果你想创建一个动态数组,你应该使用new 操作符,这样它将被存储在堆中。

        char *acBuf = new char[nsize];
        

        【讨论】:

          猜你喜欢
          • 2014-06-01
          • 1970-01-01
          • 2020-01-28
          • 2014-12-05
          • 2015-10-15
          • 2018-02-22
          • 2018-06-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多