C99′s VLAs are evil

April 7, 2009
Tags: ,

While, at first glance, the ability to define an array, whose length is determined at runtime from a variable, seems like a good idea. Hey, it’s very clean and easy to understand, and all of the normal array operations work[1]; there is one major flaw—there is no way to deal with errors.

Variable Length Arrays, in C99 are defined just like any other array, except that the length doesn’t need to be a compile-time constant:


void foo(int size) {
    int array[size];
    for(int i=0; i<size; ++i) {
        array[i] = i;
    }
    ...
}

Calling the above function with any reasonable value will do the obvious thing, the array will be sized appropriately, will contain sequential numbers. sizeof(array) will even return size*sizeof(int). For the common case, everything works, and everybody is happy. But what happens when a size is too large to fit on the stack? Who knows.[2]

In the most common implementation of VLAs the compiler literally decrements the stack pointer by size and goes on it’s merry way, without regard to whether or not the stack pointer now points to an invalid address (or worse, a valid address somewhere inside of your program’s heap). If you’re lucky, and the stack pointer points to an invalid address, your program will likely crash. On the other hand, if you’re not so lucky, the seemingly innocuous function above will start trashing things in your program’s memory space; you may not catch it, you may have a strange, untraceable crash hours later, etc.. Needless to say, this can be quite difficult to debug.

The solution is to simply not use this feature of C99. If you’ve got a higher-level abstraction (such as std::vector in C++, NSData or NSArray in Cocoa) use that. If you really want to stick with pure, standard C, use malloc()/free()[3]:


void foo(int size) {
    int *array = malloc(size * sizeof *array);
    
    if(!array) {
        //Error out here
        return;
    }
    
    for(int i=0; i<size; ++i) {
        array[i] = i;
    }
    ...
    
    free(array);
}

  1. Even sizeof works, though that required a distasteful exception to the normal sizeof rules. Before VLAs, sizeof was always evaluated at compile time, and the resulting value was always a compile-time constant. When applied to VLAs, this obviously cannot be. []
  2. I’m not joking. The official term is “undefined behavior”. In such a case, the C standard doesn’t specify anything about what happens. []
  3. Don’t be tempted to use alloca(), it is not a standard C function, and has exactly the same issue that VLAs do []