Search code examples
cc99variable-length-array

Assert the allocation of a variable-length array


I apologize for the possible duplicate (have not been able to find an answer to that):

Do we need to ensure that the allocation of a variable-length array has completed successfully?

For example:

void func(int size)
{
    int arr[size];
    if (arr == NULL)
    {
        // Exit with a failure
    }
    else
    {
        // Continue as planned
    }
}

It seems obvious that the answer is yes, but the syntax arr == NULL feels a bit unusual.

Thanks


UPDATE:

I admit to the fact that I haven't made sure that the code above even compiles (assuming that it does).

If it doesn't compile, then it means that there is no way to assert the allocation of a variable-length array.

Hence, I assume that if the allocation fails, then the program crashes immediately.

This would be a very awkward case, as it makes sense for a program to crash after an illegal memory access (read or write), but not after a non-successful memory allocation.

Or perhaps the allocation will not cause anything, but as soon as I access the array at an entry which "falls" outside the stack, I might get a memory access violation (as in a stack-overflow)...?

To be honest, I can't even see how VLAs are allocated on the stack if any more local variables follow them (other VLAs in particular), so I would appreciate an answer on that issue as well.


Solution

  • This question proceeds from a slightly flawed first premise. You cannot check if an array is NULL because, as is a popular discussion topic, an array is not a pointer in C. An array is the storage object, in-place.

    You cannot get to code where the array name is accessible without the array having been allocated. A local array is exactly the same as any other local variable: its existence is inherent and assumed for the surrounding code to be running at all, and there's no notion in the language for checking whether any given variable slot has been "allocated" at all (as the comments on the question note, "the stack" is a notion below the level C operates on - the language assumes it "happens", by unspecified magic). It has to assume this much always succeeds in order for the code to make sense on a most basic level.

    What happens in the case where the array couldn't be allocated is therefore the same as whatever happens when the runtime can't allocate space for any other local variable - the situation is inherently undefined and undefinable, because an assumption made by the C language abstract machine was violated. The language has no (fully formal) concepts that can even express this, let alone check for it or recover from it, so testing for it is similarly out of scope. Like a stack overflow, this is basically guaranteed to lead to a fatal crash.

    This does not make VLAs useless, for several reasons:

    1. Many uses of VLAs aren't going to be life-threateningly huge. Perhaps the only use of the variation is to choose a number between 3 and 5? This is no worse for space than using a few more scalar locals.

    2. Just as avoiding infinite recursion requires the programmer to prove certain properties that a C compiler doesn't, similarly you should design your program with at least a weak bound on the amount of space VLAs will be allowed to consume at any given time. For instance, you can prove to yourself that no VLA functions are ever recursive, or called from a recursive function, and none of them use more than e.g. 10K space - that's plenty useful and should be safe.

    3. You can view VLAs as an optimisation to allow you to save space where you otherwise would have had to allocate a statically-sized local array (e.g. in the first example, always allocating 5 instead of 3). As long as you know, and design around, the static upper bound, they are effectively guaranteed to make your program safer from overflow, by providing an option to not always use as much space when it isn't required.