When foo
is called, a new stack frame is pushed on the stack which reserves just enough space for all the local variables in the scope of foo
to fit. If another function bar
were to be called from within foo
, its stack frame would be pushed on top of foo
's. For this to work (efficiently), the size of the stack frame (by extension all local variables in foo
, including the array arr
) must be known to the compiler.
When the functions return (first bar
, then foo
) their stack frames are popped in reverse order of their creation, and thus the stack memory remains defragmented at all times and the location of each and every local variable is determined at compile time, hence the relative efficiency. Heap memory can have a dynamic size but this comes at the cost of having to allocate a suitably sized spot on the heap.
You can also see this dichotomy in the standard library containers std::vector
and std::array
. std::array
is effectively just a wrapper around C-style arrays and thus its size needs to be known at compile time and is therefore specified as a template parameter, e.g. std::array<char, 42>
wraps char[42]
. Unlike in your case, the compiler will (rightly) complain if you use a runtime variable as the second template parameter. You can however safely use any constant expression (e.g. result of a constexpr
function).