what happens in memory when the following happens:
char a[5];
char *b = new char[5];
Assuming a typical but somewhat simplified C++ implementation, and that the above code appears in a function:
char a[5];
The stack pointer is moved by 5 bytes, to make a 5-byte space. The name a
now refers to that block of 5 bytes of memory.
char *b = new char[5];
The stack pointer is moved by sizeof(char*)
, to make space for b
. A function is called, that goes away and allocates 5 bytes from a thing called the "free store", basically it carves 5 or more bytes off a big block of memory obtained from the OS, and does some book-keeping to ensure that when you free those bytes with delete[]
, they will be made available for future allocations to re-use. It returns the address of that allocated block of 5 bytes, which is stored into the the space on the stack for b
.
The reason that the second is more work than the first is that objects allocated with new
can be deleted in any order. Local variables (aka "objects on the stack") are always destroyed in reverse order of being created, so less book-keeping is needed. In the case of trivially-destructible types, the implementation can just move the stack pointer by the same distance in the opposite direction.
To remove some of the simplifications I made: the stack pointer isn't really moved once for each variable, possibly it's only moved once on function entry for all variables in the function, in this case the space required is at least sizeof(char*) + 5
. There may be alignment requirements on the stack pointer or the individual variables which mean it's not moved by the size required, but rather by some rounded-up amount. The implementation (usually the optimizer) can eliminate unused variables, or use registers for them instead of stack space. Probably some other things I haven't thought of.
const int len1 = random(1,5);
The language rule is reasonably simple: the size of an array must be a constant expression. If a const int
variable has an initializer in the same TU, and the initializer is a constant expression, then the variable name can be used in constant expressions. random(1,5)
is not a constant expression, hence len1
cannot be used in constant expressions. 5
is a constant expression, so len2
is fine.
What the language rule is there for, is to ensure that array sizes are known at compile time. So to move the stack, the compiler can emit an instruction equivalent to stack_pointer -= 5
(where stack_pointer
will be esp
, or r13
, or whatever). After doing that, it still "knows" exactly what offsets every variable has from the new value of the stack pointer -- 5 different from the old stack pointer. Variable stack allocations create a greater burden on the implementation.