Programming/C/Memory Model
Both C and C++ force the programmer to manage pointers and memory.
This is a manual process. This is also both a blessing and a curse.
On one hand, the programmer can potentially manage references and variable/object deconstruction (aka, what many languages refer to as "garbage collection") much better than any language that has garbage collection built-in and handled automagically.
In other words, a given C/ C++ program can potentially be magnitudes more efficient.
On the other hand, if not careful, or if they don't know what they're doing, a programmer can introduce many bugs and unintentional problems by incorrect manual garbage collection.
A set of memory can be allocated that's too small. So memory references overlap and garble data. Or data can be forgotten about, potentially hanging in memory and taking up space far far longer than it would have been around with automatic garbage collection.
Pointers and memory management is one of the biggest differences between C/C++ and any other language.
Computer Memory
A computer's memory is a series of locations called addresses. Each memory address:
- Can contain a set amount of data, represented as a given amount of 1's and 0's.
- Is represented with a unique identifier number.
The 1's and 0's are called Binary. The computer automatically converts data to binary to store it in memory, and then back to the original format, so we as humans can understand it. Similarly, the computer will automatically determine the unique identifiers that correspond to each address space.
So while it's potentially useful to understand these underlying concepts, we as programmers don't (usually) need to directly deal with these concepts.
Stack Memory Vs Heap Memory
Stack
The stack is a contiguous section of memory that contains memory for local variables. Every program has a unique stack generated at runtime.
It is called "the stack" because variables are placed in stack-order.
Aka, a program starts, and the main()
function (as well as any corresponding local variables) are immediately placed on the stack.
Any time another function is called, that is placed directly on top of the current existing stack (along with any corresponding local variables for that function).
In this way, when a function is done executing, it will always be on the top of the stack, and will thusly be popped off the stack, as it is no longer required.
This process repeats until all functions for the program have completed, and the program's main()
function finally terminates.
Due to how this works, the stack size for any given function is known at compile time. This memory is allocated at application start up.
A Stack Overflow error is when functions on the stack nest too deep, and the application runs out of the stack memory that was assigned at program startup.
Heap
The heap is a section of memory that allows variables to be dynamically allocated during runtime. This is done via calls with the new
keyword, malloc()
calls, or other similar functions.
Note that, unlike the stack, everything in the heap is manually allocated by the programmer.
Similarly, it everything in the heap should be manually unallocated when no longer used. Any instance when a program does NOT release allocated heap memory is a memory leak.
As long as the OS has enough memory available, memory of any size can be allocated.
Pointers
A pointer is simply a variable that says "there is something here at this specific location in memory, and I expect it to be of x size."
In other words, pointers are references to specific locations in memory. Any value can have a pointer created for it, regardless of it the value is on the stack or heap.
Misc Advice
- Be careful when copying any non-trivial variables, such as classes.
- Any class variables such as pointers will be copied as-is. This means when the copy is destroyed, the pointer is de-referenced for all classes.
- This would then potentially cause errors for any other copies (or the original) when trying to access the pointer or deconstruct those as well.
- With classes, it's generally best to define manually constructors and deconstructors. This can, for example, help avoid the above problem, as well as other potential problems.