Until now, the size of the data was fixed when the program was written. Dynamic allocation lets you establish it at run time, requesting memory exactly when it is needed and releasing it when it is no longer useful.
1Lab objectives
- Distinguishing static, automatic, and dynamic allocation
- Using the
malloc,calloc,realloc, andfreefunctions - Mandatory verification that allocation succeeded
- Identifying and preventing memory leaks
- Using the C++
newanddeleteoperators
2Why dynamic allocation is needed
When declaring an array, the size must be a constant known at compile time. This leads to two opposite problems:
| Situation | Consequence |
|---|---|
| An array much larger than needed is declared | memory wasted unnecessarily |
| The actual number of elements exceeds the declared size | the code must be changed and recompiled |
3Memory segments
A program's memory is divided into specialized zones, addressed through segment registers (CS, DS, ES, SS):
| Zone | What it holds | When it is allocated | When it is released |
|---|---|---|---|
| Code segment (CS) | the executable instructions | when the program loads | on termination |
| Data segment (DS) | global and static variables | at load time | on termination |
| Stack (SS) | local variables, parameters, return addresses | on entering a block | automatically, on exit |
| Heap | dynamically allocated data | on calling malloc / new | manually, through free / delete |
4C allocation functions
| Function | Prototype | Effect |
|---|---|---|
malloc | void* malloc(size_t n) | allocates n bytes, uninitialized |
calloc | void* calloc(size_t nr, size_t dim) | allocates nr×dim bytes, set to zero |
realloc | void* realloc(void* p, size_t n) | resizes an already allocated zone |
free | void free(void* p) | releases the indicated zone |
int *v = (int *)malloc(n * sizeof(int)); // 1. allocation
if (v == NULL) { // 2. mandatory CHECK
printf("Insufficient memory!\n");
return 1;
}
/* ... use ... */ // 3. use
free(v); // 4. release
v = NULL; // 5. avoid accidental use
malloc returns NULL. Using the result without checking leads to
dereferencing a null pointer and an abrupt program stop.v = realloc(v, ...) directly: if the allocation
fails, NULL is returned, and the old address is lost for good. Use a temporary variable.5Simulator: pointer to the allocated zone
Select the target to see the difference between a valid pointer, one pointing to a different zone, and one set to NULL after release.
free on the
same address corrupts the allocator's internal structures. Assigning v = NULL turns
a subtle bug into one that is immediately visible.6The C++ new and delete operators
| Operation | C | C++ |
|---|---|---|
| Allocation for a value | p = (int*)malloc(sizeof(int)); | p = new int; |
| Allocation with initialization | separate allocation + assignment | p = new int(42); |
| Allocation for an array | v = (int*)malloc(n*sizeof(int)); | v = new int[n]; |
| Releasing a value | free(p); | delete p; |
| Releasing an array | free(v); | delete[] v; |
malloc is
released with free; memory obtained with new, with delete.
Arrays must use delete[] - otherwise the elements' destructors are not called.7Classic mistakes
| Mistake | Cause | Effect |
|---|---|---|
| Memory leak | missing free | growing memory use; long-running programs eventually exhaust the system |
| Double free | releasing twice | internal allocator corruption, unpredictable crash |
| Use after free | access after release | random data or a crash; a security vulnerability |
| Buffer overflow | writing past the allocated size | overwriting neighboring heap structures |
| Unchecked allocation | missing the == NULL test | dereferencing a null pointer |
malloc must have exactly one matching
free, and every new exactly one delete. Write them in
pairs, right at the moment of allocation, so you do not forget them.8Source code
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int n;
printf("How many elements? ");
if (scanf("%d", &n) != 1 || n <= 0) {
printf("Invalid value\n");
return 1;
}
int *v = (int *)malloc(n * sizeof(int)); // exact allocation
if (v == NULL) { // mandatory check
printf("Allocation failed!\n");
return 1;
}
for (int i = 0; i < n; i++) {
printf("v[%d] = ", i);
scanf("%d", &v[i]);
}
long sum = 0;
for (int i = 0; i < n; i++) sum += v[i];
printf("\nSum = %ld, mean = %.3f\n", sum, (double)sum / n);
free(v); // release
v = NULL; // protection against accidental use
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int capacity = 4, nr = 0;
int *v = (int *)malloc(capacity * sizeof(int));
if (!v) return 1;
printf("Enter numbers (0 = stop):\n");
int x;
while (scanf("%d", &x) == 1 && x != 0) {
if (nr == capacity) { // full: double the capacity
capacity *= 2;
int *temp = (int *)realloc(v, capacity * sizeof(int));
if (temp == NULL) { // do NOT assign directly to v!
printf("Resize failed\n");
free(v);
return 1;
}
v = temp;
printf(" [capacity increased to %d]\n", capacity);
}
v[nr++] = x;
}
printf("\nYou entered %d values: ", nr);
for (int i = 0; i < nr; i++) printf("%d ", v[i]);
printf("\n");
free(v);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int rows, cols;
printf("Rows and columns: ");
scanf("%d %d", &rows, &cols);
// 1. an array of pointers to rows
int **m = (int **)malloc(rows * sizeof(int *));
if (!m) return 1;
// 2. one row for each pointer
for (int i = 0; i < rows; i++) {
m[i] = (int *)malloc(cols * sizeof(int));
if (!m[i]) { // release what was already allocated
for (int k = 0; k < i; k++) free(m[k]);
free(m);
return 1;
}
}
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
m[i][j] = (i + 1) * (j + 1); // the multiplication table
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) printf("%5d", m[i][j]);
printf("\n");
}
// release in REVERSE order of allocation
for (int i = 0; i < rows; i++) free(m[i]);
free(m);
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int n;
cout << "How many elements? ";
cin >> n;
int *v = new int[n]; // no conversion, no sizeof
for (int i = 0; i < n; i++) v[i] = (i + 1) * (i + 1);
for (int i = 0; i < n; i++) cout << v[i] << " ";
cout << endl;
delete[] v; // MANDATORY with square brackets
// allocation with initialization
int *p = new int(42);
cout << "*p = " << *p << endl;
delete p; // no brackets, for a single value
return 0;
}
9Code workshop
In Step by step mode, the panel on the right has a heap
section: the blocks allocated with malloc appear there, and you can see exactly
when they become "released".
free(v); and run again: the program
works the same way. That is exactly why memory leaks are dangerous - they are invisible. Now
move free(v); before the printing loop and you will get a clear error.10Work tasks
- Dynamically allocate an array whose size is read from the keyboard, and compute the sum and mean.
- Check what
mallocreturns for an absurdly large request (for example 10^10 bytes). - Compare the contents of zones obtained with
mallocand withcalloc, immediately after allocation. - Implement an array that doubles its capacity through
reallocwhen it fills up. - Dynamically allocate a matrix and release it correctly, in reverse order of allocation.
- Intentionally omit
freein a loop and watch memory grow in Task Manager. - Rewrite one of the programs in C++, using
newanddelete[].
11Extended application
malloc calls equals the number of free calls when the
program ends.