1. The Core Idea: Two Types of Memory
When your C program runs, it uses two main areas of memory to store variables. Understanding the difference is the key to dynamic allocation.
A. The Stack (Static Memory)
· What it is: A small, very fast, and highly organized region of memory.
·
How it works: When you call a
function, all its local variables (int
x;, char
arr[10];, etc.) are "pushed" onto the stack. When the function
ends, they are "popped" off and completely destroyed.
· Analogy: The Stack is like a stack of clean plates at a cafeteria. When you (a function) start, you take the plates you need. When you're done, you put them back, and they are gone.
·
The Rule: You MUST know the *exact size* of
all stack variables *before* the program runs (at compile time). You cannot
create an array int
arr[x]; where x is
a variable.
B. The Heap (Dynamic Memory)
· What it is: A large, unorganized pool of memory available to your entire program. It is slower to access than the Stack.
· How it works: You (the programmer) can *request* a block of memory from the Heap at *any time* while the program is running. The system gives you a pointer (an address) to that block.
·
Analogy: The Heap is like a large
self-storage facility. You can call the manager (malloc) and
say, "I need a 50-square-foot unit." They give you a key (a pointer)
to your unit.
·
The Rule: The memory you request stays
yours until you explicitly return it. If you don't return the key (free), the unit stays locked, and no one else
can use it (a "memory leak").
What is Dynamic Memory Allocation?
It is the process of manually asking the operating system for a block of memory from The Heap while your program is running.
We do this when we don't know how much memory we need at compile time, or when we need memory to "live" longer than a single function (like the nodes in a linked list).
2. The Methods: Functions for Dynamic Memory
To use the Heap,
you must include the <stdlib.h> header file. This gives you four key
functions:
1. malloc() (Memory Allocation)
2. free() (De-allocation)
3. calloc() (Contiguous Allocation)
4. realloc() (Re-allocation)
We will focus on malloc and free, which are the most important and common.
3. Using malloc() (Memory Allocation)
malloc is the "manager" of the storage facility.
You tell it *how many bytes* you need, and it gives you a key (pointer).
Syntax: ptr = (type*) malloc(total_bytes);
Let's break this down:
·
total_bytes: The
number of bytes you are requesting. We *always* use the sizeof() operator for this.
o To get
one int: sizeof(int)
o To get
an array of 10 ints: 10 * sizeof(int)
o To get
one Node for
a linked list: sizeof(struct
Node)
·
(type*): This is a cast. malloc returns a
"generic" pointer (void*). We
must tell C what kind of pointer it is (e.g., int*, struct
Node*).
· ptr: Your pointer variable that will store the address of the new memory block.
CRITICAL: Always Check for NULL
What if the storage facility (Heap) is
full? malloc can fail. If it fails, it returns NULL.
If you don't check for NULL and try to use the pointer, your
program will crash. You must always check!
4. Using free() (De-allocation)
free is how you "return the key" to your storage unit.
You give it the *same pointer* you got from malloc, and the system marks that memory as free to be used by
someone else.
Syntax: free(ptr);
CRITICAL: Avoid Memory Leaks
If you call malloc but forget to call free, that memory is "leaked." It remains unusable for the
entire time your program is running. Doing this in a loop can quickly use all
your computer's memory and crash the system.
For every malloc, there must be a matching free.
5. Example: Creating a Dynamic Array
This is the classic example. We will ask the user how many numbers they want to store, then create an array of *exactly* that size.
#include <stdio.h>
#include <stdlib.h> // For malloc and free
int main() {
int n;
int* arr; // This is our pointer (our "key")
printf("How many numbers do you want to store? ");
scanf("%d", &n);
// 1. Request memory from the Heap
// We need 'n' blocks, and each block is 'sizeof(int)' bytes
arr = (int*) malloc(n * sizeof(int));
// 2. CRITICAL: Check if malloc failed
if (arr == NULL) {
printf("Error! Memory not allocated.\n");
return 1; // Exit the program
} // 3. Use the memory just like a normal array
printf("Enter %d numbers:\n", n);
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
} // 4. Print the numbers
printf("You entered: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}printf("\n");
// 5. CRITICAL: Return the memory to the system
free(arr);
// Good practice: Set the pointer to NULL after freeing it
// to prevent it from being used by accident.
arr = NULL; return 0;
}6. Other
Methods: calloc and realloc
calloc()
·
Syntax: ptr
= (type*) calloc(n, element_size);
·
Example: arr
= (int*) calloc(n, sizeof(int));
·
Difference from malloc:
1. It takes two arguments (number of elements, size of one element).
2. It initializes
all bytes to zero. malloc leaves
the memory with garbage values.
realloc()
·
Syntax: new_ptr
= (type*) realloc(old_ptr, new_total_size);
· What it does: It tries to resize* a block of memory you already allocated.
·
Example: You mallocd an array for 10 ints, but now you need space for 20. realloc will try to extend your
block. It might have to move your data to a new, bigger location.
· This is more advanced, but it's the "method" that allows a dynamic array to "grow."