Skip to content
Go back

What the heap? (Part 2)

Published:  at  11:05 PM

Table of Contents

Open Table of Contents

Allocations

In Part 1, we saw how the heap is managed chaos. You ask for memory, and the allocator carves it up, handing you a pointer.
Now let’s build on what we saw with malloc and free and explore two more crucial heap functions: calloc and realloc.

We’ll focus on answering three new questions with our GDB experiment:

The C Code for Our Next Dive

This program will demonstrate calloc, and then show the two different behaviors of realloc. We’ll use getchar() to pause the program at key moments.
Create a file named heap2.c:

// heap_demo2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void print_bytes(void *p, int num_bytes) {
    unsigned char* ptr = p;
    for (int i=0; i < num_bytes; ++i) {
        printf("%02x ", ptr[i]);
    }
    printf("\n");
}

int main() {
    printf("--- Part 1: calloc ---\n");

    // Allocate space for 5 integers with calloc

    int *c_ptr = (int *)calloc(5, sizeof(int));
    printf("calloc allocated memory at: %p\n", c_ptr);
    printf("Bytes at c_ptr: ");
    print_bytes(c_ptr, 5 * sizeof(int));
    printf("Press Enter to continue...\n");
    getchar(); // PAUSE 1

    free(c_ptr);

    printf("\n--- Part 2: realloc (forcing a move) ---\n");

    // Allocate a 16-byte block

    char *p1 = (char *)malloc(16);
    strcpy(p1, "first block");
    printf("p1 allocated at %p with content: %s\n", p1, p1);

    // Allocate a "blocker" chunk right after it to prevent expansion

    char *p2 = (char *)malloc(16);
    printf("p2 (the blocker) allocated at %p\n", p2);

    // Now, try to reallocate p1 to a bigger size.
    // Since p2 is in the way, it will have to move.

    char *p1_new = (char *)realloc(p1, 32);
    printf("p1 was reallocated to address: %p\n", p1_new);
    printf("Content is still there: %s\n", p1_new);
    printf("Press Enter to continue...\n");
    getchar(); // PAUSE 2

    printf("\n--- Part 3: realloc (expanding in place) ---\n");

    // Now we free the blocker chunk. The space after p1_new is available.

    free(p2);
    printf("Freed the blocker chunk p2.\n");

    // Let's reallocate p1_new again. This time it should expand in place.

    char *p1_final = (char *)realloc(p1_new, 48);
    printf("p1_new was reallocated again to address: %p\n", p1_final);
    printf("Press Enter to continue...\n");
    getchar(); // PAUSE 3


    printf("\n--- Part 4: Large allocation (mmap) ---\n");

    // A large allocation is often handled by mmap, not the heap (sbrk)

    char *large_alloc = malloc(500 * 1024); // Allocate ~500KB
    printf("Large allocation is at: %p\n", large_alloc);
    printf("Check the process memory maps now!\n");
    printf("Press Enter to finish...\n");
    getchar(); // PAUSE 4

    free(p1_final);
    free(large_alloc);
    return 0;
}

As before, compile with the -g flag

gcc -g -o heap2 heap2.c

Now, just run the program. It will automatically stop at our first getchar().

./heap2

Spotted a Mistake?
Share this post on:

Previous Post
Assembly Project Ideas
Next Post
What the heap? (Part 1)