User slava had mentioned these:
automatic
, static
, thread
and dynamic
I will focus on all but thread
. He also stated this:
For C++ there is no such thing as stack or heap
User Even Teran had stated this:
I find that best way to look at heap vs. stack, is not in terms of "dynamic" or "knowing the size at compile time", (though, those can be factors). Instead, it is best to view them in terms of lifetime management.
Things allocated on the stack are destroyed when they go out of scope. Things allocated on the heap will exist until they are explicitly freed with a delete, free, or delete[] (depending on how they are allocated).
I will combine the two previous answers into a single answer with demonstrations:
int main () {
int x = 5;
{ // new scope
int x = 7;
std::cout << x << '\n'; // prints 7
} // end scope
std::cout << x << '\n'; // prints 5
return 0;
}
This is considered Automatic Storage
since the variable declarations and memory are destroyed once they go out of scope. You can consider this a stack
variable in a sense since in C++ each function
has a stack frame
. These variables x
above both live within main.cpp
's stack frame but have automatic storage
. However the first call to std::cout
will print 7
since it is within the same scope of the 2nd variable declared x
. Once the closing
brace }
is reached this scope is destroyed and so is the 2nd x
variable. So when we reach the next call to std::cout
this will print 5
because it is within the same scope of the 1st declared x
.
int main() {
int x = 5; // Automatic storage
int* ptr = nullptr; // raw pointer
ptr = new int(x); // Dynamic Storage
std::cout << "x = " << x << '\n'; // prints 5
std::cout << "*ptr = " << *ptr << '\n'; // prints 5
{ // new scope
*ptr = 12;
} // end scope
std::cout << "x = " << x << '\n'; // x unchanged prints 5
std::cout << "*ptr = " << *ptr << '\n'; // prints 12
delete ptr; // clean up memory
return 0;
}
Dynamic Storage
lives longer than the scope it is declared in. You can pass dynamic memory from one function to another as long as the original object that it references is still valid otherwise you will have a memory leak, invalid or dangling pointer which can lead the unwanted behavior, undefined behavior, program crash, your OS erased and nuclear bombs going off! (Well the last is a bit dramatic; but don't laugh because poor memory management will reek havoc on your code base and ALL WHO use it!). This allows you to store data and to modify it from one function to another without having to create copies upon copies of it every time you need to reference it in some other calculation.
static int i = 0; // static storage: life time of application or file scope; similar to a global...
void add1() {
i++;
}
int main() {
std::cout << i << '\n';
add1();
std::cout << i << '\n';
for ( int n = 0; n < 10; n++ ) {
add1();
std::cout << "n=" << n << ": " << i << '\n';
}
return 0;
}
Output
0
1
n=0: 2
n=1: 3
n=2: 4
n=3: 5
n=4: 6
n=5: 7
n=6: 8
n=7: 9
n=8: 10
n=9: 11
Static Storage
is a little different. There is no need to clean it up like dynamic memory since it has properties similar to Automatic Storage
as it will be destroyed automatically. However, these are typically seen in global namespace
or global file space
they can be useful, but global variables again can be dangerous if not managed nor used properly. These typically have the lifetime of either the program application if defined in main.cpp
or the lifetime of file-scope if defined in some other cpp file. The only other difference with Static Storage
is it is also only initialized once, and there is usually only ever one instance of it!
Yes there are different types of storage classes and many still refer to them as stack
and heap
mainly cause C++
was built off of C
; but the meanings and usages have change dramatically over the years. Anymore in C++
it has more to do with the lifetime and visibility of a variable than where
it is located in memory.