I am working on a piece of legacy code (no tests). I stumbled upon a section hidden inside several macros. It generates a warning if compiled with GCC's -Wvla
.
The code in question is equivalent to what can be seen in this small program:
typedef struct entry {
unsigned index;
unsigned reserved;
unsigned value;
} entry_t;
int main(int argc, char **argv) {
long pa = 0;
long res = pa + sizeof(entry_t[10 - argc]);
return res;
}
When compiled, it gives out a warning:
$ gcc -g -Wvla repro-vla.c
repro-vla.c: In function ‘main’:
repro-vla.c:9:5: warning: ISO C90 forbids variable length array [-Wvla]
9 | long res = pa + sizeof(entry_t[10 - argc]);
| ^~~~
The culprit is of course this expression: sizeof(entry_t[10 - argc])
. The syntax is a bit confusing here. I believe a temporary anonymous array for 10 - argc
entries of type entry_t
is created, then its size is taken, and the array is discarded.
My questions are:
sizeof(entry_t) * (10-argc)
? Both calculate the same value, and neither one does anything to guard against the underflow (when argc >= 10
). The second expression does not use variable length arrays and as such won't generate the warning, and it is also easier to comprehend in my opinion.No temporary array object is created.
There is a common misconception that VLA is about stack allocated arrays of length defined in runtime, a form of a bit safer alloca()
.
No. VLAs are about typing not storage. The following line is the essence of "VLA-ness":
typedef int T[n];
not:
int A[n];
Note that VLA type declarations do not allocate any storage and the can be used without any concerns of running out of stack. The VLA types are very useful for handling multidimensional arrays and expressing access ranges in functions arguments i.e. void foo(int n, int arr[n]);
. VLA types were optional in C11 but they will be mandatory in C23 due to their usefulness.
The expression sizeof(entry_t[10 - argc]
is essentially the same as:
typedef entry_t _unnamed_type[10 - argc];
sizeof(_unnamed_type)
No VLA array object is created there. I think that the problem is -Wvla
flag itself. The -Wvla
warns about any declaration of VLA type (not VLA object) what is overzealous because it also catches the good usages of VLA types. There is a request to add -Wvla-stack-allocation
warning to clang to catch dangerous usages of VLAs. Alternatively, one can use gcc's -Wvla-larger-than=0
but it does not work very well.