Can C declare the size of array at runtime?
Historically, C does not allow the size of an array to be declared at runtime. One of my colleagues asked the question in code review, which obviously puzzled him, since he’s rightly remembered that you shouldn’t be able to do that. My fuzzy recollection from my Borland Turbo-C days agree with him, but this doesn’t mean you can’t do it in GCC today.
I hadn’t check if it’s something from C98, or is it a GNU extension, but GCC does have no problems with generating code that allocates dynamically a variable-sized array at runtime, as long as it’s not initialized. Consider the following code:
#include <stdio.h>
int main()
{
int x = 16;
char y[ x ];
printf( "%d\n", y[ 9 ] );
}
GCC will compile it without a problem. A slight modification to initialize it:
#include <stdio.h>
int main()
{
int x = 16;
char y[ x ] = { 0 };
printf( "%d\n", y[ 9 ] );
}
And the compiler will complain:
test.cpp: In function 'int main()':
test.cpp:6: error: variable-sized object 'y' may not be initialized
Rightly so, because you cannot allocate space in .bss
dynamically. But for
uninitialized memory, GCC can allocate it from the stack dynamically like alloca()
.
The disassembly compiling with gcc -O0
shows what exactly gets generated:
0000000000400614 <main>:
400614: 55 push %rbp
400615: 48 89 e5 mov %rsp,%rbp
400618: 53 push %rbx
400619: 48 83 ec 28 sub $0x28,%rsp
40061d: 48 89 e0 mov %rsp,%rax
400620: 48 89 c3 mov %rax,%rbx
400623: c7 45 ec 10 00 00 00 movl $0x10,-0x14(%rbp)
40062a: 8b 45 ec mov -0x14(%rbp),%eax
40062d: 48 98 cltq
40062f: 48 83 e8 01 sub $0x1,%rax
400633: 48 89 45 d8 mov %rax,-0x28(%rbp)
400637: 48 83 c0 01 add $0x1,%rax
40063b: 48 83 c0 0f add $0xf,%rax
40063f: 48 83 c0 0f add $0xf,%rax
400643: 48 c1 e8 04 shr $0x4,%rax
400647: 48 c1 e0 04 shl $0x4,%rax
40064b: 48 29 c4 sub %rax,%rsp <= DYNAMIC STACK ADJUSTMENT
40064e: 48 89 e0 mov %rsp,%rax
400651: 48 83 c0 0f add $0xf,%rax
400655: 48 c1 e8 04 shr $0x4,%rax
400659: 48 c1 e0 04 shl $0x4,%rax
40065d: 48 89 45 e0 mov %rax,-0x20(%rbp)
400661: 48 8b 45 e0 mov -0x20(%rbp),%rax
400665: 0f b6 40 09 movzbl 0x9(%rax),%eax
400669: 0f be c0 movsbl %al,%eax
40066c: 89 c6 mov %eax,%esi
40066e: bf 98 07 40 00 mov $0x400798,%edi
400673: b8 00 00 00 00 mov $0x0,%eax
400678: e8 73 fe ff ff callq 4004f0 <printf@plt>
40067d: eb 0e jmp 40068d <main+0x79>
40067f: 48 89 dc mov %rbx,%rsp
400682: 48 63 d2 movslq %edx,%rdx
400685: 48 89 c7 mov %rax,%rdi
400688: e8 93 fe ff ff callq 400520 <_Unwind_Resume@plt>
40068d: 48 89 dc mov %rbx,%rsp
400690: b8 00 00 00 00 mov $0x0,%eax
400695: 48 8b 5d f8 mov -0x8(%rbp),%rbx
400699: c9 leaveq
40069a: c3 retq
40069b: 90 nop
40069c: 90 nop
40069d: 90 nop
40069e: 90 nop
40069f: 90 nop
The compiler does stack alignment, before performing the arithmetic to reserve the size dynamically from the stack, which is pretty neat.
Note: it’s usually not very safe to allocate more than 4K blocks and addressing the memory location past the 4K page. The stack memory is lazily allocated by the (Linux) kernel, and any arbitrary access for more than a page-size can result in hard page faults and mysterious crashes.