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.
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).