Implementing realloc

hard · memory, reallocation, edge-cases

Realloc with Data Preservation

realloc is deceptively complex. It must handle:

  • Growing allocations (may need to move)
  • Shrinking allocations (can stay in place)
  • Edge cases: NULL pointer, zero size

Semantics

void *realloc(void *ptr, size_t new_size);
  • If ptr is NULL: equivalent to malloc(new_size)
  • If new_size is 0: implementation-defined (we'll treat as free(ptr) and return NULL)
  • If new_size <= current_size: may shrink in place, return same pointer
  • If new_size > current_size: allocate new block, copy data, free old block

Critical: If realloc fails (can't allocate new size), it returns NULL but the original block must remain valid and unchanged.

Your Task

// Reallocate a memory block to a new size
// Preserves data up to min(old_size, new_size)
void *my_realloc(void *ptr, size_t new_size);

// Helper: copy n bytes from src to dst
// You implement this - do not use memcpy
void my_memcpy(void *dst, const void *src, size_t n);

// Helper: get the size of an allocated block
size_t get_block_size(void *ptr);

Subtle Issues

Data Preservation

When growing, only copy the OLD size worth of data:

// If old block was 100 bytes, new request is 200 bytes
// Copy exactly 100 bytes, not 200!
my_memcpy(new_ptr, old_ptr, old_size);

The Return Value Contract

void *p = malloc(100);
fill_with_data(p);

void *q = realloc(p, 200);
if (q == NULL) {
    // CRITICAL: p is still valid! Data is preserved!
    // Many programs forget this and leak or corrupt here
}
// If q != NULL: p is now invalid, use only q

In-Place Shrinking

When shrinking, you don't need to allocate new memory:

if (new_size <= block->size) {
    // Optionally update block->size to new_size
    // Optionally split the remainder (covered in later problem)
    return ptr;  // Same pointer, less logical size
}

Overlap Safety

When copying, the old and new regions must not overlap (for realloc they won't - you allocate new then copy). But memcpy requires non-overlapping regions. Since realloc allocates fresh memory before copying, this is safe.

Edge Cases

  1. realloc(NULL, 100) - just malloc
  2. realloc(ptr, 0) - free and return NULL
  3. realloc(ptr, same_size) - return same pointer
  4. realloc(ptr, larger) - malloc new, copy, free old
  5. realloc(ptr, smaller) - may return same pointer

Memory Leak Trap

// WRONG - leaks if realloc fails
ptr = realloc(ptr, new_size);  // If returns NULL, old ptr is lost!

// CORRECT
void *new_ptr = realloc(ptr, new_size);
if (new_ptr != NULL) {
    ptr = new_ptr;
}
Run tests to see results
No issues detected