What Is Memory
Memory is the physical storage where a program’s instructions, data, and state live while it runs.
The CPU does not “see” variables or objects.
It sees addresses and bytes.
Everything in C and C++—variables, arrays, objects, stacks, heaps—exists only as:
- a memory address
- a size
- a lifetime
Understanding memory allocation is therefore not an “advanced topic” — it is the foundation of correct, fast, and safe software.
The Process Memory Layout
A typical C/C++ program is divided into regions:
| Region | Purpose | Lifetime |
|---|---|---|
| Text / Code | Compiled instructions | Entire program |
| Data | Global & static initialized data | Entire program |
| BSS | Global & static uninitialized data | Entire program |
| Stack | Function calls, local variables | Scope-based |
| Heap | Dynamic allocation | Programmer-controlled |
This naturally leads to two fundamental allocation models:
- Static allocation (Compile time)
- Dynamic allocation (Run time)
Choosing _how_ memory is allocated directly affects:
- Performance
- Determinism
- Safety
- Cache behavior
- Fragmentation
- Scalability
Rule of thumb:
Memory mistakes in C/C++ don’t fail loudly — they fail silently.
So let’s start where memory begins.
Static Memory Allocation
it is the data in Data, BSS memory segments.
Static memory allocation means:
- Memory size is known at compile time
- Memory address is fixed
- Memory lifetime is entire program execution
No runtime decision.
No allocation overhead.
No failure.
Use static allocation when:
- Size is known and fixed
- Lifetime spans the whole program
- Determinism is required (embedded / RTOS)
- Performance is critical
- Memory must never fail
Static Allocation in C
Global Variables
int global_counter = 0;
- Allocated in Data segment
- Exists from program start to exit
Static Global (Internal Linkage)
static int file_private_counter;
- Visible only in this translation unit
- Still static lifetime
Static Local Variables
void increment(void) {
static int count = 0;
count++;
printf("%d\n", count);
}
Why this exists:
To preserve state across function calls without heap allocation.
Static Arrays
static int buffer[1024];
- Fixed size
- Fast access
- Zero fragmentation
Stack Allocation in C
assigned variables
int a = 5;
pointers: which is a variable that stores the memory address.
Wild pointer: the un-intilized pointer.
void *ptr;
Null pointer: pointer points to nothing = null or 0
void *ptr = ((void *)0);
void *ptr = NULL;
Dangling Pointer: when the object deleted or de-allocated, and the pointer still exist.
Static Allocation in C++
C++ adds object lifetime semantics on top.
Static Objects
struct Logger {
Logger() { std::cout << "Init\n"; }
~Logger() { std::cout << "Destroy\n"; }
};
static Logger logger;
- Constructor runs before
main() - Destructor runs after
main() - Order across translation units is not guaranteed
> Static Initialization Order Fiasco, This problem alone is why large C++ systems avoid global statics.
Why Static Allocation Is Fast:
- No system calls
- No locks
- No bookkeeping
- Perfect cache locality
- Zero fragmentation
But nothing is free.
Limitations of Static Allocation
Static memory cannot:
- Grow or shrink
- Be conditionally created
- Be freed early
- Handle unknown sizes
So what happens when size or lifetime is not known?
We move to dynamic allocation.
Dynamic Memory Allocation
Dynamic allocation means:
- Memory size decided at runtime
- Memory taken from the heap
- Lifetime controlled manually
This gives flexibility — but introduces risk.
You need dynamic allocation when:
- Size depends on input or runtime state
- Lifetime exceeds a single scope
- Objects must outlive function calls
- Data structures grow (lists, trees, maps)
Dynamic Allocation in C
malloc
Allocate memory from heap but does not initialize it.
int* arr = (int*)malloc(10 * sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed\n");
return 1;
}
- Uninitialized memory
- Fast but dangerous
calloc
Allocate memory from heap and initializes it to 0
int* arr = (int*)calloc(10, sizeof(int));
- Zero-initialized
- Slightly slower
realloc
Resize an existing allocated memory block which is already allocated before.
arr = realloc(arr, 20 * sizeof(int));
- May move memory
- Pointer may change
- Old pointer invalid if moved
free
free(arr);
arr = NULL;
Every `malloc` must have **exactly one** `free`.
Dynamic Allocation in C++
`new` Operator
The new operator allocates memory for an object or array of objects from the heap and returns a pointer to the allocated memory.
// Allocates memory for a single integer
int* ptr = new int;
int* p = new int(42); // 42 is initial value
// Allocates memory and calls the constructor for MyClass
MyClass* obj = new MyClass();
// Array
int* arr = new int[10];
`delete` Operator
The delete operator deallocates memory that was previously allocated with new.
If you allocated an array with new[], you must use delete[] to deallocate it.
// Deallocates memory for a single integer
delete ptr;
// Deallocates memory for an array
delete[] arr;
// Deallocates memory and calls the destructor for MyClass
delete obj;
`delete` vs `delete[]` mismatch is **undefined behavior**
Why `new` Is Not Just `malloc`
C++ new does:
- Allocate memory
- Call constructor
- Handle exceptions
malloc does none of this.
The Real Problem with Manual Dynamic Allocation
Dynamic memory introduces:
- Memory leaks
- Double frees
- Use-after-free
- Heap corruption
- Fragmentation
- Cache misses
This is why modern C++ changed the game by implementing RAII.
RAII — The Bridge Between Static and Dynamic
RAII is Resource Acquisition Is Initialization, Memory is tied to object lifetime, not manual calls.
{
std::vector<int> v(1000);
} // memory freed automatically here
No delete.
No leaks.
No ambiguity.