运行时检查失败 #4 - 此函数保留的 _alloca 内存周围的堆栈区域已损坏?

Sta*_*123 2 c++ stack runtime-error alloca

#include <iostream>
#include <malloc.h>

void print_vals(int n)
{
    int *arr = (int *)alloca(n);

    for (int i = 0; i < n; i++)
        arr[i] = i;

    for (int i = 0; i < n; i++)
        std::cout << arr[i] << ' ';

    std::cout << '\n';
}

int main()
{
    print_vals(5);
    print_vals(10);
}
Run Code Online (Sandbox Code Playgroud)

当我运行此代码时,每次调用都会收到此错误:

Run-Time Check Failure #4 - Stack area around _alloca memory reserved by this function is corrupted
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明 我使用的是 Visual C++ 2019,stdc++14 和 stdc++17 都会产生相同的错误。

这段代码有什么问题?

pax*_*blo 8

我将假设这只是示例代码,因为如果您的目的只是打印数字0..n,那么您绝对没有理由为此使用数组:-)


关于您的实际问题,该alloca函数(如malloc)分配了许多字节。如果然后将其视为具有多个int值(几乎可以肯定大于一个字节),那将不会有好的结局。这实际上已在您看到的对话框中确认,其中大小为 10,但每个数组元素占用 4:

Size: 10
Data: <> 00 00 00 00 01 00 00 00 02 00
         \_________/ \_________/ \____
            arr[0]      arr[1]      ar
Run Code Online (Sandbox Code Playgroud)

您需要做的是:

int *arr = (int*) alloca(n * sizeof(*arr)); // n ints, not bytes.
Run Code Online (Sandbox Code Playgroud)

无论如何,我都会避免, alloca因为:

  1. 这真的不标准;和
  2. 如果它不能分配空间,它有一个令人讨厌的习惯,即行为不端,而不是像new那样引发异常。

最后一点,Linux doco foralloca states(我的重点):

alloca() 函数返回一个指向已分配空间开头的指针。如果分配导致堆栈溢出,则程序行为未定义。

如果你想要一些你不必担心自己取消分配的东西,现代 C++ 有智能指针,所以你可以使用类似的东西:

std::unique_ptr<int[]> arr(new int[n]);
Run Code Online (Sandbox Code Playgroud)

当该arr变量超出范围时,为其分配的内存将自动释放。您可以在原始代码的完整变体中尝试:

#include <iostream>
#include <memory>

void print_vals(size_t n) {
    std::unique_ptr<int[]> arr(new int[n]);

    for (size_t i = 0; i < n; i++)
        arr[i] = static_cast<int>(i);
    for (size_t i = 0; i < n; i++)
        std::cout << arr[i] << ' ';
    std::cout << '\n';
}

int main() {
    print_vals(5);
    print_vals(10);
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句,您会注意到我也改为使用size_t而不是int大小和索引 - 我相信这更合适。现在,当然,这使用而不是堆栈,但根据之前关于alloca.

可以alloca一点更容易成功,如果你保证的大小限制的一些,比如有:

void print_vals(size_t n) {
    if (n >= 100) {
        doSomethingIntelligent();
        return;
    }
    int *arr = (int *)alloca(n * sizeof(*arr));

    ...
}
Run Code Online (Sandbox Code Playgroud)

但这实际上并不能保证其安全性,如果您打算这样做,您最好还是使用固定缓冲区,因为您将知道最大大小:

void print_vals(size_t n) {
    int arr[100];
    if (n >= sizeof(arr) / sizeof(*arr)) {
        doSomethingIntelligent();
        return;
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

我想提出的另一种解决方案是:如果您的意图是尽可能高效,则可以使用混合方法。我的意思是使用低于特定大小的本地缓冲区并仅在需要时分配内存:

void print_vals(size_t n) {
    // Default to using local buffer.

    int localBuff[100];
    int *arr = localBuff;

    // If more space needed, allocate  an auto-freeing smart pointer.

    std::unique_ptr<int[]> allocBuff;
    if (n >= sizeof(localBuff) / sizeof(*localBuff)) {
        allocBuff.reset(new int[n]);
        arr = allocBuff.get();
    }

    // Here, arr points to to a big-enough buffer.

    ...
Run Code Online (Sandbox Code Playgroud)

  • @StackExchange123,你正在做未定义的行为。这意味着未定义,就像“它甚至可以*工作,*至少在一段时间内”:-)可能发生的情况是,它可以自由地写入和读取分配空间之外的内存,但是,当需要退出时功能,发生腐败。实际上,你*幸运*它抓住了它,而不是使用堆栈上可能损坏的返回值返回到“main”。 (4认同)