ποΈ Dynamic Memory: Your Programβs Personal Warehouse
Imagine youβre running a toy store. Sometimes you need 5 boxes to store toys, sometimes 100! You canβt know exactly how many youβll need before opening the store. Thatβs exactly what Dynamic Memory does for your programs!
πΊοΈ Memory Layout: Where Everything Lives
Think of your computerβs memory like a big apartment building:
βββββββββββββββββββββββββββββββ β Top (High Address)
β STACK β π₯ Plates pile here
β (grows downward β) β
βββββββββββββββββββββββββββββββ€
β β
β (empty space) β
β β
βββββββββββββββββββββββββββββββ€
β HEAP β π¦ Boxes go here
β (grows upward β) β
βββββββββββββββββββββββββββββββ€
β Uninitialized Data β (global vars = 0)
βββββββββββββββββββββββββββββββ€
β Initialized Data β (global vars with values)
βββββββββββββββββββββββββββββββ€
β CODE β π Your program's instructions
βββββββββββββββββββββββββββββββ β Bottom (Low Address)
Simple Rule: Your program lives in an organized building with different floors for different things!
π₯ Stack vs π¦ Heap: Two Ways to Store
π₯ The Stack (Like a Pile of Plates)
When mom washes plates, she stacks them. The last plate added is the first one taken. Thatβs the stack!
void makeFood() {
int plates = 5; // Put on stack
int cups = 3; // Put on stack
} // Plates and cups automatically removed!
Stack Facts:
- β Super fast (just add/remove from top)
- β Automatic cleanup (no mess!)
- β Fixed size (limited space)
- β Canβt keep things after function ends
π¦ The Heap (Like a Warehouse)
Imagine a HUGE warehouse where you can:
- Request any size box whenever you want
- Keep boxes as long as you need
- But you MUST return the box yourself!
int* getBox() {
int* box = malloc(sizeof(int)); // Get box from warehouse
*box = 42;
return box; // Box stays! We have the key!
}
Heap Facts:
- β Get any size, any time
- β Keep it as long as you need
- β Slower (warehouse manager needs to find space)
- β You MUST clean up yourself!
π malloc: βMay I Have a Box, Please?β
malloc = Memory ALLOCation = βGive me this much space!β
#include <stdlib.h>
// Get space for 1 integer
int* one_num = malloc(sizeof(int));
// Get space for 10 integers (an array!)
int* ten_nums = malloc(10 * sizeof(int));
// ALWAYS check if you got the box!
if (one_num == NULL) {
printf("Warehouse is full! π±\n");
}
malloc gives you:
- β Raw space (whatever was there before)
- π A pointer (the βkeyβ to your box)
- β οΈ NULL if no space available
graph TD A["You: malloc#40;16#41;"] --> B{Warehouse<br>Manager} B -->|Found space!| C["Here&#39;s your key π<br>#40;pointer#41;"] B -->|No space!| D["NULL π«<br>#40;no box#41;"]
π§Ή calloc: βClean Box, Please!β
calloc = Contiguous ALLOCation = βGive me boxes, but wash them first!β
// Get 5 clean integers (all set to 0!)
int* clean_nums = calloc(5, sizeof(int));
// This is like malloc + memset:
// int* nums = malloc(5 * sizeof(int));
// memset(nums, 0, 5 * sizeof(int));
calloc vs malloc:
| Feature | malloc | calloc |
|---|---|---|
| Takes | total bytes | count Γ size |
| Contents | garbage ποΈ | zeros 0οΈβ£ |
| Speed | faster | slightly slower |
When to use calloc:
- Arrays that need to start at 0
- Structures you want βemptyβ
- When garbage values are dangerous!
π realloc: βI Need a BIGGER Box!β
Your toy collection grew! You need more space! realloc to the rescue!
// Started with 5 toys
int* toys = malloc(5 * sizeof(int));
toys[0] = 1; toys[1] = 2; // Some toys stored
// Need space for 10 now!
int* bigger = realloc(toys, 10 * sizeof(int));
if (bigger != NULL) {
toys = bigger; // Use new, bigger box!
// Old data (1, 2) is still there!
}
graph TD A["toys β π¦ size 5<br>[1][2][?][?][?]"] --> B["realloc#40;toys, 10#41;"] B --> C["new β π¦π¦ size 10<br>[1][2][?][?][?][?][?][?][?][?]"] C --> D["Old box returned β»οΈ"]
realloc Magic:
- β Keeps your old data
- β Can grow OR shrink
- β οΈ Might move your box (new pointer!)
- π‘
realloc(NULL, size)=malloc(size)
ποΈ free: βIβm Done With This Boxβ
THE MOST IMPORTANT FUNCTION!
Every malloc/calloc/realloc needs a matching free!
int* data = malloc(100);
// ... use data ...
free(data); // Return box to warehouse!
data = NULL; // Forget the old key! πβ
Why set to NULL?
free(data);
// data still points somewhere! (Dangerous!)
data = NULL; // Now it's safely "nothing"
The Golden Rule:
π― For every
malloc, there must be exactly ONEfree!
π§ Memory Leaks: The Forgotten Boxes
Imagine ordering boxes but never returning them. Soon, the warehouse is full of YOUR boxes that you forgot about!
void leaky_function() {
int* leak = malloc(1000);
// Oops! We never freed it!
} // Function ends, pointer gone, memory stuck!
Every time this runs, 1000 bytes are lost forever (until program ends)!
graph TD A["π Run leaky_function"] --> B["π¦ 1000 bytes allocated"] B --> C["πͺ Function ends"] C --> D["π Pointer lost!"] D --> E["π¦ Memory STUCK!<br>Can&#39;t use, can&#39;t free"]
Leak Symptoms:
- π Program gets slower
- πΎ Memory usage keeps growing
- π₯ Eventually crashes (out of memory!)
How to Prevent:
void safe_function() {
int* safe = malloc(1000);
// ... use it ...
free(safe); // Return the box!
safe = NULL; // Safety first!
}
π Double Free: The Dangerous Mistake
What happens if you return a box thatβs already returned? CHAOS!
int* data = malloc(100);
free(data); // Good! Box returned.
free(data); // BAD! Box already returned! π₯
Why is this terrible?
- The warehouse might have given that box to someone else
- Youβre now messing with THEIR box!
- Program crashes or worse: security holes!
graph TD A["malloc β π¦ at address 1000"] --> B["free#40;data#41; β <br>Box 1000 returned"] B --> C["malloc #40;someone else#41;<br>Gets box 1000!"] C --> D["free#40;data#41; again π₯<br>You free THEIR box!"] D --> E["π± CRASH or<br>πΎ Security Disaster"]
The Double Free Shield:
int* data = malloc(100);
free(data);
data = NULL; // π‘οΈ Shield activated!
// Later...
free(data); // free(NULL) is safe! Does nothing.
π‘ Pro Tip:
free(NULL)is perfectly safeβit does nothing!
π― The Complete Picture
#include <stdio.h>
#include <stdlib.h>
int main() {
// 1οΈβ£ malloc: Get raw box
int* raw = malloc(sizeof(int));
*raw = 42;
// 2οΈβ£ calloc: Get clean boxes
int* clean = calloc(5, sizeof(int));
// clean[0] to clean[4] are all 0!
// 3οΈβ£ realloc: Resize box
int* bigger = realloc(clean, 10 * sizeof(int));
if (bigger) clean = bigger;
// 4οΈβ£ free: Return ALL boxes!
free(raw);
raw = NULL;
free(clean);
clean = NULL;
// β
No leaks! No double frees!
return 0;
}
π Quick Reference
| Function | Purpose | Returns | Example |
|---|---|---|---|
malloc(size) |
Get raw memory | pointer or NULL | malloc(100) |
calloc(n, size) |
Get zeroed memory | pointer or NULL | calloc(5, sizeof(int)) |
realloc(ptr, size) |
Resize memory | pointer or NULL | realloc(arr, 200) |
free(ptr) |
Return memory | nothing | free(arr) |
π Memory Master Checklist
- [ ] Always check if
malloc/calloc/reallocreturns NULL - [ ] Every allocation needs exactly ONE
free - [ ] Set pointers to NULL after freeing
- [ ] Never use a pointer after freeing it
- [ ] Never free the same pointer twice
- [ ] Remember: Stack = automatic, Heap = manual
Youβve Got This! π Dynamic memory is like being a responsible warehouse manager. Get what you need, use it well, and always return it when done!
