I wrote a C program that accepts integer input from the user, that is used as the size of an integer array, and using that value it declares an array of given size, and I am confirming it by checking the size of the array.
Code:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int n;
scanf("%d",&n);
int k[n];
printf("%ld",sizeof(k));
return 0;
}
and surprisingly it is correct! The program is able to create the array of required size.
But all static memory allocation is done at compile time, and during compile time the value of n
is not known, so how come the compiler is able to allocate memory of required size?
If we can allocate the required memory just like that then what is the use of dynamic allocation using malloc()
and calloc()
?
The memory for this construct, which is called "variable length array", VLA, is allocated on the stack, in a similar way to
alloca
. Exactly how this happens depends on exactly which compiler you're using, but essentially it's a case of calculating the size when it is known, and then subtracting [1] the total size from the stack-pointer.You do need
malloc
and friends because this allocation "dies" when you leave the function. [And it's not valid in standard C++][1] For typical processors that use a stack that "grows towards zero".
In C, the means by which a compiler supports VLAs (variable length arrays) is up to the compiler - it doesn't have to use
malloc()
, and can (and often does) use what is sometimes called "stack" memory - e.g. using system specific functions likealloca()
that are not part of standard C. If it does use stack, the maximum size of an array is typically much smaller than is possible usingmalloc()
, because modern operating systems allow programs a much smaller quota of stack memory.This is not a "static memory allocation". Your array
k
is a Variable Length Array (VLA), which means that memory for this array is allocated at run time. The size will be determined by the run-time value ofn
.The language specification does not dictate any specific allocation mechanism, but in a typical implementation your
k
will usually end up being a simpleint *
pointer with the actual memory block being allocated on the stack at run time.For a VLA
sizeof
operator is evaluated at run time as well, which is why you obtain the correct value from it in your experiment. Just use%zu
(not%ld
) to print values of typesize_t
.The primary purpose of
malloc
(and other dynamic memory allocation functions) is to override the scope-based lifetime rules, which apply to local objects. I.e. memory allocated withmalloc
remains allocated "forever", or until you explicitly deallocate it withfree
. Memory allocated withmalloc
does not get automatically deallocated at the end of the block.VLA, as in your example, does not provide this "scope-defeating" functionality. Your array
k
still obeys regular scope-based lifetime rules: its lifetime ends at the end of the block. For this reason, in general case, VLA cannot possibly replacemalloc
and other dynamic memory allocation functions.But in specific cases when you don't need to "defeat scope" and just use
malloc
to allocate a run-time sized array, VLA might indeed be seen as a replacement formalloc
. Just keep in mind, again, that VLAs are typically allocated on the stack and allocating large chunks of memory on the stack to this day remains a rather questionable programming practice.Memory for variable length arrays clearly can't be statically allocated. It can however be allocated on the stack. Generally this involves the use of a "frame pointer" to keep track of the location of the functions stack frame in the face of dynamicly determined changes to the stack pointer.
When I try to compile your program it seems that what actually happens is that the variable length array got optimised out. So I modified your code to force the compiler to actually allocate the array.
Godbolt compiling for arm using gcc 6.3 (using arm because I can read arm ASM) compiles this to https://godbolt.org/g/5ZnHfa. (comments mine)
When it is said that the compiler allocates memory for variables at compile time, it means that the placement of those variables is decided upon and embedded in the executable code that the compiler generates, not that the compiler is making space for them available while it works. The actual dynamic memory allocation is carried out by the generated program when it runs.