As far as the Standard is concerned there is no notion of stack and heap. However, all C++ implementation that I know of will map the concepts "automatic storage duration" and "dynamic storage" into stack (*) and heap respectively.
(*) As remarked by @MooingDuck
, this is only true for function variables. Globals and static variables (probably) have automatic storage duration, yet they are not on the stack.
Now this is cleared:
- variables in a function body are stored on the stack
- objects created by
new
are stored in the heap (and their address returned)
With examples, to be a bit more visual:
void f0() {
Class* c = new Class();
}
void f1() {
Class* c = 0;
c = new Class();
}
Here c
(of type Class*
) is stored on the stack and points to an object (of type Class
) that is stored on the heap
void f2() {
Class c = Class();
}
void f3() {
Class c;
}
Here c
is stored on the stack. In f2
there might be a temporary (object without name) created by the expression Class()
and then copied into c
(depending on whether the compiler elides the copy or not), the storage of temporaries is not addressed by the Standard... they commonly use the stack though.
A final word: whether this ends up actually using some space on the stack or not is another matter.
- The compiler may completely elide the need for the object
- Variables may be stored either on the stack or in registers (CPU specific "slots")
In action:
// Simple test.cpp
#include <cstdio>
struct Class { void foo(int& a) { a += 1; } };
int main() {
Class c;
int a = 0;
c.foo(a);
printf("%d", a);
}
The compiler (using Clang/LLVM... slightly reworked) generates:
@.str = private unnamed_addr constant [3 x i8] c"%d\00", align 1
define i32 @main() nounwind uwtable {
%1 = tail call i32 (i8*, ...)* @printf(@.str, i32 1)
ret i32 0
}
Note how: 1. The class has been removed, 2. The call to foo
has been removed, 3. a
does not even appear. Translated back to C++ we get:
#include <cstdio>
int main() {
printf("%d", 1);
}
And if we generate the assembly (64-bit X86):
main: # @main
pushq %rax # save content of 'rax' on the stack
movl $.L.str, %edi # move address of "%d" into the 'edi' register
movl $1, %esi # move 1 into the 'esi' register
xorb %al, %al # --
callq printf # call printf, it'll look up its parameters in registers
xorl %eax, %eax # --
popq %rdx # restore content from stack to 'rdx'
ret # return
Note how constants ($1
and $.L.str
) are pushed into registers (%esi
and %esi
resp.) and never "hit" the stack. The only stack manipulations are pushq
and popq
(and I have no idea what they actually save/restore.