Language 1 of 10 · Topic 0.8

Memory Architecture: Stack vs Heap (malloc, calloc, realloc, free)

1Concept

C memory is divided into Stack (fast, automatic, fixed size) and Heap (dynamic, manual lifetime). Dynamic memory is allocated via malloc(size) (uninitialized) or calloc(num, size) (zero-initialized), resized with realloc(), and MUST be freed with free(). Failing to call free() causes memory leaks; using memory after free() causes undefined behavior (use-after-free).

2Architecture Diagram

+------------------------------------+ High Memory (0xFFFFFFFF)
| Stack (Grows Downwards)            | Local stack frames
|      |                             |
|      v                             |
|                                    |
|      ^                             |
|      |                             |
| Heap (Grows Upwards - malloc/free) | Dynamic memory
+------------------------------------+
| BSS Segment (Uninitialized globals)| Zeroed on start
+------------------------------------+
| Data Segment (Initialized globals) | Global/static vars
+------------------------------------+
| Text Segment (Machine Code)        | Read-only instructions
+------------------------------------+ Low Memory (0x00000000)

3Code Example

Stage 0 Language Foundations
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int num_elements = 4;
    
    // Allocate zero-initialized memory on Heap
    int *arr = (int *)calloc(num_elements, sizeof(int));
    if (arr == NULL) {
        fprintf(stderr, "Memory allocation failed!\n");
        return EXIT_FAILURE;
    }

    for (int i = 0; i < num_elements; i++) {
        arr[i] = (i + 1) * 10;
        printf("arr[%d] = %d (at address %p)\n", i, arr[i], (void*)&arr[i]);
    }

    // Resize heap memory to hold 6 elements
    int *temp = (int *)realloc(arr, 6 * sizeof(int));
    if (temp == NULL) {
        free(arr);
        return EXIT_FAILURE;
    }
    arr = temp;
    arr[4] = 50; arr[5] = 60;
    printf("After realloc, arr[5] = %d\n", arr[5]);

    // Mandatory cleanup
    free(arr);
    arr = NULL; // Prevent dangling pointer

    return EXIT_SUCCESS;
}

4Expected Output

arr[0] = 10 (at address 0x1a2b00)
arr[1] = 20 (at address 0x1a2b04)
arr[2] = 30 (at address 0x1a2b08)
arr[3] = 40 (at address 0x1a2b0c)
After realloc, arr[5] = 60

5Key Takeaways

  • Always check if malloc/calloc returned NULL before dereferencing.
  • Always set pointer to NULL after calling free(ptr).
  • Every malloc/calloc call must have exactly one corresponding free() call.