This might seem like a beginner\'s question, but I am interested in the way that a compiler normally creates arrays of variable-dimensions, like in the following program.
int main(){
int n;
std::cin>>n;
int a[n];
}
This is not legal C++. G++ accepts Variable Length Array's as an extension but VLAs are not part of the C++ standard. They are part of the C99 standard which GCC supports(I am not sure to what extent) but MSVC doesn't.
There's absolutely no problem with "subtracting the stack pointer" at run-time, by a run-time value. And that is how the compilers normally implement variable-length arrays. They "subtract the stack pointer" at run-time, when the actual array size is already known. That's all there is to it. (There's no need to allocate memory on heap and I don't know what made you suspect this in GCC.).
Such functionality was available long before VLAs became part of the language. The [non-standard] function alloca
does exactly that. The only difference is that the memory allocated by alloca
is automatically deallocated when the function exits, while the local VLAs must obey standard block-based lifetime rules. The latter is not a problem at all, since block nest in a stack-like fashion.
In other words, for a run-time value n
the declaration
int a[n];
is essentially translated into something like
int *a = alloca(n * sizeof *a);
plus some extra household data to support the functionality of sizeof
etc (and, of course, automatic restoration of the original stack pointer value at the end of the enclosing block).
No version of C++ allows variable length array. Only C99 allows it.
GCC allows it as an extension.
In the C99 version of the C standard, variable length arrays are permitted. However, they are not permitted in any version of C++; you're seeing a G++ extension. Note that Microsoft's C compiler does not fully support C99; since G++ supports C99 it's easy enough to apply the VLA support to C++ as an extension.
As to how the compiler usually implements VLAs, it's the same as alloca()
(except that it has to keep the size around for sizeof
) - the compiler saves the original stack pointer, then adjusts it down by however many bytes it calculates that it needs. The downside is function entry and exit is a bit more complicated as the compiler needs to store where to reset the stack pointer to rather than just adjusting by fixed constants.