Search code examples
c++stackruntime-erroralloca

Run-Time Check Failure #4 - Stack area around _alloca memory reserved by this function is corrupted?


#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);
}

When I run this code, I get this error per call:

Run-Time Check Failure #4 - Stack area around _alloca memory reserved by this function is corrupted

enter image description here I'm using Visual C++ 2019, both stdc++14 and stdc++17 produce the same error.

What's wrong with this code?


Solution

  • I'm going to assume this is just sample code since, if your intent is just to print the numbers 0..n, there's absolutely no reason why you would use an array for this :-)


    On to your actual problem, the alloca function, like malloc, allocates a number of bytes. If you then treat it as if you have a number of int values (which are almost certainly larger than a byte), that won't end well. That's actually confirmed in the dialog box you see where the size is ten but each array element takes up four:

    Size: 10
    Data: <> 00 00 00 00 01 00 00 00 02 00
             \_________/ \_________/ \____
                arr[0]      arr[1]      ar
    

    What you would need to do is something like:

    int *arr = (int*) alloca(n * sizeof(*arr)); // n ints, not bytes.
    

    In any case, I would be avoiding alloca because:

    1. It's really not standard; and
    2. It has the annoying habit of misbehaving if it cannot allocate the space, rather than raising an exception as new would.

    On that last point, the Linux doco for alloca states (my emphasis):

    The alloca() function returns a pointer to the beginning of the allocated space. If the allocation causes stack overflow, program behavior is undefined.

    If you want something where you don't have to worry about de-allocating it yourself, modern C++ has smart pointers, so you could use something like:

    std::unique_ptr<int[]> arr(new int[n]);
    

    When that arr variable goes out of scope, the memory that was allocated for it will be automagically released. You can try that in this complete variation on your original code:

    #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);
    }
    

    As an aside, you'll notice I've also changed to using size_t rather than int for the size and indexes - I believe that's a better fit. Now, granted, this uses the heap rather than the stack, but it's the safer way to do it, based on the earlier comments about alloca.

    You could make alloca a little more likely to succeed if you guaranteed some limit on the size, such as with:

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

    But that doesn't actually guarantee its safety and, if you're going to do that, you may as well use a fixed buffer anyway since you'll know the maximum size:

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

    One other solution I'd like to put forward: if your intent is to be as efficient as possible, you could use a hybrid approach. By that, I mean using a local buffer for below a certain size and allocating memory only if needed:

    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.
    
        ...