malloc with Block Reuse

Lesson, slides, and applied problem sets.

View Slides

Lesson

Advanced Operations

Beyond malloc and free, we need realloc and calloc.

realloc: Resizing Allocations

void *realloc(void *ptr, size_t new_size);

Cases to Handle

  1. ptr is NULL: Act like malloc(new_size)
  2. new_size is 0: Act like free(ptr), return NULL
  3. Shrinking: Can return same pointer
  4. Growing: May need to move data

Implementation

void *my_realloc(void *ptr, size_t new_size) {
    // Case 1: NULL pointer
    if (ptr == NULL) {
        return my_malloc(new_size);
    }

    // Case 2: Zero size
    if (new_size == 0) {
        my_free(ptr);
        return NULL;
    }

    block_header_t *block = payload_to_block(ptr);
    size_t old_size = block->size;

    // Case 3: Shrinking or same
    if (new_size <= old_size) {
        return ptr;  // Keep same block
    }

    // Case 4: Growing
    void *new_ptr = my_malloc(new_size);
    if (new_ptr == NULL) {
        return NULL;  // OLD BLOCK STAYS VALID!
    }

    memcpy(new_ptr, ptr, old_size);  // Copy OLD size
    my_free(ptr);

    return new_ptr;
}

Critical Detail: Failure Behavior

char *p = malloc(100);
fill_data(p);

char *q = realloc(p, 200);
if (q == NULL) {
    // realloc failed, BUT:
    // - p is still valid!
    // - p's data is intact!
    // Can continue using p
}

If realloc fails, the original pointer must remain valid. Never do:

p = realloc(p, new_size);  // WRONG: loses p if realloc fails!

calloc: Zero-Initialized Memory

void *calloc(size_t count, size_t size);

Allocates count * size bytes, all initialized to zero.

void *my_calloc(size_t count, size_t size) {
    size_t total = count * size;

    // Check for overflow
    if (count != 0 && total / count != size) {
        return NULL;  // Overflow!
    }

    void *ptr = my_malloc(total);
    if (ptr != NULL) {
        memset(ptr, 0, total);
    }

    return ptr;
}

Overflow Check

Why check for overflow?

calloc(SIZE_MAX, 2);  // SIZE_MAX * 2 overflows!

Without the check, you'd allocate a tiny amount and return it, causing buffer overflows.

memcpy Implementation

You might need your own:

void my_memcpy(void *dst, const void *src, size_t n) {
    char *d = dst;
    const char *s = src;
    for (size_t i = 0; i < n; i++) {
        d[i] = s[i];
    }
}

For realloc, src and dst don't overlap (new allocation), so simple copy is safe.


Module Items