3

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.

barak manos
  • 29,648
  • 10
  • 62
  • 114
  • Local variables are allocated on the stack. If it can't allocate it, the program will probably crash before you test it. – Barmar Jul 27 '14 at 13:40
  • @Barmar: From what I read on VLAs, the standard does not define whether they are allocated on the stack or on the heap. And if "that thing" indeed crashes immediately, then it makes this mechanism pretty useless. – barak manos Jul 27 '14 at 13:42
  • You're right, the standard doesn't mandate it. But I expect it's how most implementations do it. A local array can never be `NULL`. – Barmar Jul 27 '14 at 13:43
  • @Barmar: That's why I emphasized that the syntax "feels a bit unusual". You seem to be coming from the same point of view as my own, but I'm pretty sure that we're both wrong here (i.e., this type of assertion is legitimate and even imperative if one wishes to ensure the safe execution of their program). – barak manos Jul 27 '14 at 13:45
  • Stack Overflow is UB, so you are SOL. Just make sure you don't need too much stack beforehand. – Deduplicator Jul 27 '14 at 13:55
  • @Deduplicator: From what I read on VLAs, the standard does not define whether they are allocated on the stack or on the heap. – barak manos Jul 27 '14 at 13:56
  • @barakmanos: Yes, because it does not define "stack" like, at all (=> no `auto`-storage-class variable need live on a stack). Well, a VLA is an automatic variable and there are no mechanisms at all for reporting an error there, it's just UB. – Deduplicator Jul 27 '14 at 13:58
  • @Deduplicator: I think I've got it. What you're saying is, 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)? – barak manos Jul 27 '14 at 14:01
  • Not quite. Executing the VLA definition makes this execution of the program UB if it cannot be satisfied, just like for any `auto`-variable, call-frame or what have you. Fun fact: UB involves time-travel, so it can reach back to the start of the program. – Deduplicator Jul 27 '14 at 14:05
  • 1
    @Deduplicator: Doesn't that essentially make VLA pretty useless in every possible context? – barak manos Jul 27 '14 at 14:06
  • Nope. It's still useful, but it's a power-tool with its own set of dangers. Excessive use of stack-space is always bad, no matter how you got there. – Deduplicator Jul 27 '14 at 14:07
  • @Deduplicator: I can't even see how VLAs are allocated on the stack if any other local variables follow them (more VLAs in particular). – barak manos Jul 27 '14 at 14:08
  • 2
    Compiler magic, what else? Early x86 (probably others) most times used a frame-pointer in addition to a stack-pointer. Nowadays that's omitted where possible... So, the compiler probably does some magic reviving that concept there... – Deduplicator Jul 27 '14 at 14:12
  • If the allocation of a VLA fails, the behavior is undefined. There is no standardized way to test for failure, but if you are lucky, your program will crash. See http://stackoverflow.com/a/5543571/139746 – Pascal Cuoq Jul 27 '14 at 17:35
  • @barakmanos: An implementation may offer guarantees sufficient to know that certain sizes of allocations will succeed in certain contexts, but implementations are not required to offer any such guarantees. An implementation could be conforming even if it were incapable of generating useful code for any function that included a variable of a VLA type. I suspect that's part of the reason they were made an optional feature in C11--if code can't support such declarations usefully, a compiler error would often be better than bogus code generation. – supercat Aug 31 '16 at 19:44

1 Answers1

3

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.

Community
  • 1
  • 1
Alex Celeste
  • 12,824
  • 10
  • 46
  • 89
  • I knew that this question would get me into trouble with people trying to explain that "an array is not a pointer in C". You could have inferred from the statement "the syntax `arr == NULL` feels a bit unusual" that I am well aware of that. But a VLA is not an ordinary array. For example (as I mentioned at the end of the question), I am pretty sure that it is allocated on the stack in a different manner than a "normal" array. – barak manos Jul 27 '14 at 16:07
  • In addition, here is a link on Wikipedia stating that a VLA may be allocated on the heap, in which case, it is essentially a pointer (unless I misunderstood what it says): http://en.wikipedia.org/wiki/Variable-length_array. – barak manos Jul 27 '14 at 16:08
  • You're confusing the language and its implementation. An array *is not a pointer*. It doesn't matter if a local array is allocated on the heap - that's the machine's business. C doesn't know that there is a heap (C also doesn't know about memory addresses). How many layers of indirection the compiler chooses to insert is immaterial; from the language's abstract machine POV, there are none, and that means the language can't express any. – Alex Celeste Jul 27 '14 at 16:21
  • OK, so it's "what happens when the runtime can't allocate space for any other local variable" which I have doubt about: As far as I can think of, nothing happens until an attempt to access (read or write) this local variable is made. Is it any different with a VLA? In other words, will the UB occur immediately after it is allocated, or only once a memory access operation is attempted? – barak manos Jul 27 '14 at 16:34
  • In addition, local-variable allocation does not yield any additional assembly code. Is it any different with VLAs? I cannot really imagine how a VLA would be allocated without the compiler adding some assembly code. If that is indeed the case, then will UB occur within that piece of code (i.e., **before** any memory access operations are attempted)? – barak manos Jul 27 '14 at 16:37
  • (Depending on the platform of course) - "allocation" is usually just incrementing a pointer that describes the end of the available memory. Since this doesn't actually touch said memory, nothing will happen at that point (you could have a check on the increment, but this would cripple performance in the normal case). You'll normally get a segfault when you try to read or write it though. You might even be able to catch and recover from this with a signal handler on `SIGSEGV`. This sort of thing is out of the hands of the standard, though - plan for it with your platform's documentation. – Alex Celeste Jul 27 '14 at 16:50
  • ...but ^ is when the *segfault* occurs... UB is another language abstract. As the above illustrates, depending on how you allocate stack space, things could go wrong at multiple different points. – Alex Celeste Jul 27 '14 at 16:53