pointers-memory · intermediate · ~22 min

Grow a buffer and zero only the new region

Realloc + explicit zero-fill, with attention to OOM.

Challenge

Resize a heap buffer with realloc, then zero only the newly added bytes. realloc leaves the extended region uninitialised — it may contain stale heap data — so zeroing just the new part is the safe building block.

Task

Implement void *grow_zeroed(void *ptr, size_t old_size, size_t new_size) that calls realloc(ptr, new_size), zeroes the bytes from old_size up to new_size when growing, and returns the new pointer. No main — the grader calls it.

Input

ptr — the existing block (may be NULL, in which case this acts like malloc). old_size — its current size in bytes. new_size — the desired size in bytes.

Output

Returns the resized pointer, with bytes [old_size, new_size) set to 0 when growing. Returns NULL on allocation failure — in that case the original ptr is still valid and must NOT be freed.

Example

char *p = malloc(4); memcpy(p, "abc", 4);
tmp = grow_zeroed(p, 4, 16)    ->   "abc\0" preserved, bytes 4..15 all zero

char *p = malloc(16); memset(p, 'X', 16);
tmp = grow_zeroed(p, 16, 8)    ->   shrunk; no zero-fill needed

Edge cases

  • ptr == NULL: behaves like malloc(new_size) then zeroes it.
  • Shrinking (new_size <= old_size): no zero-fill.
  • Allocation failure: return NULL, leave the original block alive.

Rules

  • realloc into a temporary so an OOM doesn't lose (and leak) the original.
  • Zero only the new region, not the whole buffer.

For a nonzero request, store the result in a temporary and adopt it only after success. If it is NULL, keep and eventually free the original owner.

Why this matters

Transfer exercise: apply the lesson to a complete function contract.

Input format

ptr — the existing block (may be NULL, in which case this acts like malloc). old_size — its current size in bytes. new_size — the desired size in bytes.

Output format

Returns the resized pointer, with bytes [old_size, new_size) set to 0 when growing. Returns NULL on allocation failure — in that case the original ptr is still valid and must NOT be freed.

Constraints

For new_size=0 explicitly free ptr and return NULL. Otherwise ptr owns old_size bytes (old_size=0 when ptr is NULL). Failure returns NULL leaving ptr valid. Success preserves old bytes and zeroes only the added region; caller adopts the result.

Starter code

#include <stddef.h>
void *grow_zeroed(void *ptr, size_t old_size, size_t new_size) { /* TODO */ return NULL; }

Common mistakes

Losing the old owner or zeroing preserved bytes; relying on realloc(p,0).

Edge cases to handle

NULL/zero old allocation; shrink; equal sizes; grow; explicit zero new size; allocation failure.

Complexity

Amortised O(new_size - old_size) for the zero-fill plus realloc cost.

Background lessons

Up next

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.