Dynamic Memory Management
Memory in a C program can be divided into four categories:
- Code memory
- Static data memory
- Runtime stack memory
- Heap memory
Code Memory
Code memory is used to store machine instructions. As a program runs, machine instructions are read from memory and executed.
Static Data Memory
Static data memory is used to store static data. There are two categories of static data: global and static variables.
Global variables are variables defined outside the scope of any function as
can be seen in example 1. Static variables on the other hand are defined with
the static
modifier as seen in example 2.
Both global and static variables have one value attached to them; they are
assigned memory once; and they are initialised before main
begins execution
and will continue to exist until the end of execution.
Example 1: Global variables.
int counter = 0;
int increment(void) {
counter++;
return counter;
}
Example 2: Static variables.
int increment(void) {
// will be initialised once
static int counter = 0;
// increments every time the function is called
counter++;
return counter;
}
Runtime Stack Memory
Runtime stack memory is used by function calls and is FILO (First in, Last out). When a function is invoked, a block of memory is allocated by the runtime stack to store the information about the function call. This block of memory is termed as an Activation Record.
The information about the function call includes:
- Return address.
- Internal registers and other machine-specific information.
- Parameters.
- Local variables.
Heap Memory
Heap memory is memory that is allocated during the runtime of the program. On many systems, the heap is allocated in an opposite direction to the stack and grows towards the stack as more is allocated. On simple systems without memory protection, this can cause the heap and stack to collide if too much memory is allocated to either one.
To deal with this, C provides two functions in the standard library to handle
dynamic memory allocation; calloc()
(contiguous allocation) and malloc()
(memory allocation).
void *calloc(size_t n, size_t s)
returns a pointer to enough space in memory
to store n
objects, each of s
bytes. The storage set aside is automatically
initialised to zero.
void *malloc(size_t s)
returns a pointer to a space of size s
and leaves the
memory uninitialised.
Example 3: malloc()
and calloc()
#include <stdio.h>
#include <stdlib.h>
int main() {
int num_of_elements;
int *ptr;
int sum = 0;
printf("Enter number of elements: ");
scanf("%d", &num_of_elements);
ptr = malloc(num_of_elements * sizeof(int));
// or
// ptr = calloc(num_of_elements, sizeof(int));
if (ptr == NULL) {
printf("[Error] - Memory was unable to be allocated.");
exit(0);
}
printf("Enter elements: ");
for (int i = 0; i < n; i++) {
scanf("%d", ptr + i);
sum += *(ptr + i);
}
printf("Sum = %d", sum);
free(ptr);
return 0;
}