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.