14

I heard from many people that variable length array, introduced in C99, are terrible. Some guys on IRC said a minute ago « I don't think C++ will get VLA's, strousoup made some very negative comments about them ».

What are the reasons why those people hate VLAs?

qdii
  • 12,505
  • 10
  • 59
  • 116
  • 1
    I hear that this could be seen as a troll question. Please don’t think it is, I am just looking for reasons to prefer std::vector or such. – qdii Sep 13 '12 at 13:51
  • Possible duplicate: http://stackoverflow.com/questions/1887097/variable-length-arrays-in-c – m0skit0 Sep 13 '12 at 13:51
  • 1
    Didn't C11 even deprecate VLAs or something? – Xeo Sep 13 '12 at 14:04
  • 6
    @Xeo Not deprecated, they've been made optional "Variable length arrays are a conditional feature that implementations need not support; see 6.10.8.3.". – Daniel Fischer Sep 13 '12 at 14:07
  • @DanielFischer Wow, that's always best. "This is a standard-guaranteed feature, except that the standard doesn't guarantee it's presence." – Christian Rau Sep 13 '12 at 14:49
  • 1
    @ChristianRau, there are several parts of the C standard that are optional and can be tested with macros. I find nothing shocking in that. – Jens Gustedt Sep 13 '12 at 14:50
  • @qdii, if you don't want it to be seen as troll question, you'd have to formulate it much more carefully. – Jens Gustedt Sep 13 '12 at 14:52
  • @ChristianRau Like the `(u)intN_t` types of `stdint.h`. And those are unquestionably **very** useful. – Daniel Fischer Sep 13 '12 at 14:52
  • @JensGustedt Really, what are those, can you name some (seriously, I don't have much C experience)? – Christian Rau Sep 13 '12 at 14:53
  • @DanielFischer Ah right, the infamous optional `uintN_t`s, completely forgot about those. And their optionality is indeed really bugging me (of course we know every platform has them anway, but well), to an extent that I prefer `uint_leastN_t` if possible. Yes they're extremely useful, which makes their optionality even more of a problem. – Christian Rau Sep 13 '12 at 14:55
  • @ChristianRau Making them mandatory would effectively prohibit C implementations on hardware with 9-bit bytes (or other esoteric sizes). The committee isn't willing to tie C to 8-bits-per-byte two's complement hardware. – Daniel Fischer Sep 13 '12 at 15:02
  • @ChristianRau, Daniel already gave the example of the fixed width types. Then there are `[u]intptr_t`, complex numbers, support for atomic operations, thread support, IEC 559 floating point arithmetic, type genereric math functions. I probably forgot some :) – Jens Gustedt Sep 13 '12 at 15:03
  • 1
    `"Making them mandatory would effectively prohibit C implementations on hardware with 9-bit bytes"` And utter crap like that sums up why ISO standards are _bad_, they aren't allowed to favour a certain technology. Imagine how good C would be if byte sizes and int type sizes were strictly specified, if signed integers were always two's complement, if float numbers had a designated type for each float number representation, if Unicode was the only allowed symbol table etc etc. ISO is doing such a great job in their role as a firewall protecting us against sane, technical improvements. – Lundin Sep 13 '12 at 15:29
  • @Lundin While I agree that it'd be nice to have byte and integer sizes plus some representations of types guaranteed, I still prefer the status quo over having to put up with MISRA (_shudders at the thought_) ;) – Daniel Fischer Sep 13 '12 at 16:08
  • @Lundin The language would be awful if it required those things and a new generation of hardware was developed where those were incredibly inefficient. We'd all have to abandon it and use something else. The fact that ISO isn't standardizing something doesn't mean they're somehow acting as a firewall to prevent it from being developed. Not everything needs to be in a single monolithic standard to be useful. – bames53 Sep 13 '12 at 17:52
  • @Lundin: "*ISO is doing such a great job in their role as a firewall protecting us against sane, technical improvements.*" What you propose would be a firewall against technical *hardware* improvements. The first thing you do with new hardware is boostrap a C compiler, so you can compile your actual code on it. Pretty much nobody makes hardware that *can't* run C. Thus, ISO C can't introduce any elements that prevent hardware makers from coming up with new, innovative designs just because those designs don't revolve around 8-bit bytes and 2's compliment integers. – Nicol Bolas Sep 13 '12 at 20:53
  • @bames53 If you have ever worked with hardware-related programming, you would know that the various hardware platforms don't give a damn about the C standard most of the time. They invent their own language mechanisms, even when there are perfectly fine ones available in C, they invent horrid hardware mechanisms such as "read register to clear flag", GPIO registers at address 0, skip static initialization entirely etc etc. Since they already have no qualms about giving the finger to the standard, I don't think you have to worry about them suddenly caring about it in the future. – Lundin Sep 14 '12 at 06:29
  • @NicolBolas See comment above. And yes, plenty of companies make hardware that can't run C, then invent plenty of non-standard language mechanisms to make their hardware work on "almost C". – Lundin Sep 14 '12 at 06:31
  • @DanielFischer I always ask those who are sceptical against MISRA-C: what code standard are you using then, which is so much better? Which static analysis tool are you using, which has better ways to prevent bugs than the present MISRA-C checkers? People who can't answer those questions are likely further away from becoming professional programmers than they thought they were. – Lundin Sep 14 '12 at 06:37
  • @Lundin I use Aleister Crowley's standard: Do what thou wilt. And I'm rather confident I'm exactly as far from becoming a professional programmer as I think (slim chance, not one of my goals). On a more serious note, the vast majority of MISRA's rules are good in themselves, every reasonable person follows them anyway, but of course they belong in a coding standard. Of the remaining, a lot are good for ensuring portability of code, those are fine too. But there are a few that I find absolutely unbearable. No comma operator?? No unary minus on unsigned types???? No `continue`???????? – Daniel Fischer Sep 14 '12 at 10:55
  • @Lundin There are many platforms that do care about the standard and I'm glad those are the only ones I develop for. If the platforms you use don't implement the standard anyway then what's the point of complaining about ISO? Even if what you wanted were standardized the platforms you're talking about apparently still wouldn't free compelled to implement those features. – bames53 Sep 14 '12 at 13:45

4 Answers4

10

VLAs allocate arrays on the stack, in runtime, making it harder, or even impossible to determine the stack size used at compile time. Since the stack has a rather small amount of memory available (in comparison with the heap), many worry that VLAs have a great potential for stack overflow.

The upcoming version of the MISRA-C coding standard is most likely going to ban VLAs as well.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • No, one can easily create VLA on heap. See https://stackoverflow.com/a/54163435/4989451 – tstanisl Sep 16 '20 at 07:49
  • 1
    @tstanisl You misunderstand that (mostly theoretical) post. In the example with malloc, they create _a pointer to VLA_ pointing to allocated memory. The pointer is then passed to a function accepting a VLA parameter. That parameter decays into a pointer to VLA. There is not an actual VLA allocated in either case. Pointers to VLA are _fine_, but allocating actual VLAs with the VLA array syntax is mildly useful. Note that pointer to VLA are consistent and compatible with pointer to (static) array types. – Lundin Sep 16 '20 at 08:48
  • 8 years later after this answer was posted, we now know that both the C11 and MISRA-C:2012 committees were both sceptical to VLA overall. Therefore C11 made them optional, MISRA-C banned them completely. Both failed to understand the usefulness of pointer to VLA, they can be used to create clearer and far more type safe code. – Lundin Sep 16 '20 at 08:49
  • A lot depends on what one understands by VLA. Either an object (read data) or an array type of variable size. Anyway VLA objects can be easily created on *heap* as show in example where a pointer to VLA object is assigned to an *allocated* VLA object with `malloc()`. VLAs is not about the storage (static, automatic, dynamic, etc) but rather about the types that have runtime-defined size. – tstanisl Sep 16 '20 at 10:02
  • @tstanisl C17 6.7.6.2 Array declarators describe you declare a variable length type: "If an identifier is declared as having a variably modified type, it shall be an ordinary identifier (as defined in 6.2.3), have no linkage, and have either block scope or function prototype scope. If an identifier is declared to be an object with static or thread storage duration, it shall not have a variable length array type." – Lundin Sep 16 '20 at 10:15
  • @tstanisl It doesn't mention allocated storage. But in case of dynamic memory, I could as well have a `bananas_t*` point at the result of malloc - that wouldn't turn the allocated memory into bananas, nor into bananas of variable length. The C rules for effective type state that the allocated storage only gets an effective type when a lvalue access stores something in that memory. Most likely that will be an `int` or such, and then the compiler doesn't know what aggregate type (if any) that `int` is part of. – Lundin Sep 16 '20 at 10:16
7

Although variable-length arrays have their problems, one should keep in mind how they came to be: As a replacement for alloca(), which is arguably even more problematic.

While it was trivial to implement on the PDP-11, this was not the case on other architectures and Ritchie and Thompson removed it from their implementation.

However, variably-sized automatic allocation was apparently useful enough that alloca() got resurrected despite it's problems (in particular, it can't be used everywhere where arbitrary function calls would be possible and on many architectures it must be a compiler built-in anyway). The C working group agreed with providing such a feature, but thought variable-length arrays the superior solution.

If you look at the features added with C99 (complex numbers, type-generic math, restrict, ...), you should notice that a lot of them are geared towards making C a better language for numeric computation. Variable-length arrays are useful there as well and I believe Fortran already had them at that time. Furthermore, their introduction also led to variably-modified derived types (eg pointers to variably-sized arrays), which are particularly useful when dealing with matrices.

Christoph
  • 164,997
  • 36
  • 182
  • 240
  • If there were a function `stalloc()` that was like alloca() but required that `stfree()` be called in LIFO order to clean up allocations before a function exits via any means, that would have been implementable on any platform. Support for abandoning blocks via longjmp() might be problematic and should thus be optional, but the semantics of a LIFO allocator would be much more versatile than those associated with VLAs. – supercat Oct 10 '16 at 18:54
6

As others have pointed out, VLAs make it really easy to overflow your stack frame. I'm not a compiler writer, but my understanding is that VLAs can also be a bugger to support (they are now optional in C2011). And their use is limited to block or function scope; you cannot use a VLA at file scope, and they can't have external linkage.

I wouldn't want to see VLA syntax go away, though; it comes in really handy when dynamically allocating multi-dimensional arrays where the inner dimensions are not known until runtime, such as:

size_t r, c;
// get values for r and c
int (*arr)[c] = malloc(r * sizeof *arr);
if (arr)
{
   ...
   arr[i][j] = ...;
   ...
   free(arr);
}

One contiguous allocation (and one corresponding free), and I can subscript it as a 2D array. The alternatives usually mean piecemeal allocation:

size_t r, c;
...
int **arr = malloc(sizeof *arr * r);
if (arr)
{
  for (i = 0; i < c; i++)
    arr[i] = malloc(sizeof *arr[i] * c);
  ...
  arr[i][j] = ...;
  ...
  for (i = 0; i < c; i++)
    free(arr[i]);
  free(arr);
}

or using 1-d offsets:

int *arr = malloc(sizeof *arr * r * c);
if (arr)
{
  ...
  arr[i * r + j] = ...;
  ...
  free(arr);
}
John Bode
  • 119,563
  • 19
  • 122
  • 198
2

VLAs make it much easier to overflow the stack. In most places where you would use a VLA, you would base the length on one of the functions parameters. If the parameter is something you don't expect, you could end up allocating a very large array on the stack. Unless you can be sure that no combination of arguments can cause you to overflow the stack, you should use dynamic allocation.

One place it might make sense to use them is on embedded platforms, since when doing embedded programming is one of the few situations where you are likely to keep track of your memory usage closely enough to be sure that you won't have a stack overflow.

Dirk Holsopple
  • 8,731
  • 1
  • 24
  • 37