Reserving memory is known as allocation, and the method varies based on the memory type.
In C, there are two types of memory: static memory and dynamic memory.
Static memory is memory that is allocated for variables before the program execution. This allocation process is also referred to as compile-time memory allocation.
In C, memory for every variable is automatically allocated during the compilation of the program.
For instance, if you declare an integer array for 20 students (e.g., for a summer semester), C will reserve space for 20 elements, typically occupying 80 bytes of memory (20 * 4 bytes).
int students[20]; printf(“%lu”, sizeof(students)); // 80 bytes |
However, when the semester begins, it becomes apparent that only 12 students are attending. This results in the wastage of space for the 8 unused elements.
As the size of the array cannot be altered dynamically, you are left with unnecessary reserved memory.
It’s important to note that the program will still execute without any damage. However, if your program contains numerous instances of such code, it may run slower than it could optimally.
For more precise management of allocated memory, explore Dynamic Memory below.
Dynamic memory refers to memory allocated after the program commences execution. This allocation process is also known as runtime memory allocation.
In contrast to static memory, dynamic memory grants you full control over the memory usage at any given moment. You have the flexibility to write code that determines the required memory amount and allocates it accordingly.
Dynamic memory is not associated with a specific variable and can only be accessed via pointers.
To allocate dynamic memory, you can utilize the malloc() or calloc() functions. It’s essential to include the <stdlib.h> header to access them. Both malloc() and calloc() functions allocate memory and return a pointer to its address.
int *ptr1 = malloc(size); int *ptr2 = calloc(amount, size); |
The malloc() function takes a single parameter, size, indicating the amount of memory to allocate, measured in bytes.
The calloc() function, on the other hand, requires two parameters:
Please be aware: The content in the memory allocated by malloc() is unpredictable. To prevent unexpected values, ensure that you write something into the memory before reading from it. In contrast to malloc(), the calloc() function initializes all allocated memory locations with zeroes. However, this feature renders calloc() slightly less efficient. |
Using the sizeof operator is the most effective method for allocating the correct amount of memory for a given data type.
int *ptr1, *ptr2; ptr1 = malloc(sizeof(*ptr1)); ptr2 = calloc(1, sizeof(*ptr2)); |
Take caution: using sizeof(*ptr1) instructs C to measure the size of the data at the address pointed to by ptr1. However, if you forget the * and write sizeof(ptr1) instead, it will measure the size of the pointer itself, typically 8 bytes required to store a memory address. Note: The sizeof operator cannot determine the amount of dynamic memory allocated. When assessing dynamic memory, it solely provides the size of the data type. For instance, if you reserve space for 5 float values, the sizeof operator will return 4, representing the number of bytes needed for a single float value. |
Let’s utilize dynamic memory to enhance the student example mentioned earlier.
As mentioned before, we cannot employ sizeof to determine the allocated memory’s size; instead, we need to compute it by multiplying the number of items by the size of the data type.
int *students; int numStudents = 12; students = calloc(numStudents, sizeof(*students)); printf(“%d”, numStudents * sizeof(*students)); // 48 bytes |
When engaging in dynamic memory allocation, it’s imperative to incorporate error checking and memory deallocation at the conclusion of the program. You’ll delve deeper into these concepts in the upcoming chapters.
To provide a comprehensive overview, it’s important to mention stack memory. Stack memory, a form of dynamic memory, is reserved for variables declared within functions. Variables declared inside a function utilize stack memory rather than static memory.
Upon calling a function, stack memory is allocated for the variables within that function. Once the function completes its execution and returns, the stack memory is released.
Understanding stack memory is crucial for managing memory usage in scenarios involving nested function calls and recursion. Excessive recursion may consume excessive stack memory, leading to a condition known as a stack overflow.