5.1.14 · D5C Programming
Question bank — Dynamic memory — malloc, calloc, realloc, free
True or false — justify
malloc initializes the memory it returns to zero.
False.
malloc returns uninitialized (garbage) bytes; only calloc guarantees zeros. Fresh pages may look zeroed by luck of the OS, but reused heap memory holds old contents.After free(p), the variable p becomes NULL automatically.
False.
free returns the block to the allocator but does not touch your pointer variable; p still holds the same (now dangling) address. You must set p = NULL; yourself.free(NULL) is a dangerous mistake that may crash.
False. Calling
free(NULL) is explicitly defined as a harmless no-op by the C standard, which is exactly why nulling a pointer after freeing makes an accidental second free safe.The C standard guarantees calloc returns NULL if count * size overflows.
False. Overflow checking in
calloc is implementation-defined. Glibc, musl, etc. do check in practice, but a conforming minimal libc need not — so treat it as a bonus, not a portable guarantee.realloc(NULL, n) is undefined behavior.
False.
realloc(NULL, n) is defined to behave exactly like malloc(n). This lets you write generic grow-loops that work even on the first call when the pointer is still NULL.realloc(p, 0) frees p and reliably returns NULL.
False (subtle). It may free
p, but whether it returns NULL or a tiny valid pointer is implementation-defined. Don't rely on the return value to mean "freed" — use free for freeing.Memory allocated on the heap is automatically reclaimed when a function returns.
False. Only stack (automatic) variables vanish at function return. Heap blocks live until you
free them or the program exits — see The Stack and the Heap.If malloc fails it returns a non-null but invalid pointer.
False.
malloc returns exactly NULL on failure; there is no "invalid non-null" sentinel. That's why a single if (p == NULL) check is sufficient.sizeof(*p) and sizeof(p) compute the same value.
False.
sizeof(*p) is the size of the object p points to (one element); sizeof(p) is the size of the pointer itself (typically 8 bytes on 64-bit). Confusing them under-allocates — see sizeof operator.Growing a buffer by adding one slot each time is as efficient as doubling.
False. Adding one forces a copy on nearly every push, giving total work. Doubling gives amortized because copies become geometrically rare.
Spot the error
int *p = malloc(n); for(i=0;i<n;i++) p[i]=i;
Under-allocation. You asked for
n bytes but store n ints — you need n * sizeof(*p) bytes. Writing past the block is buffer overflow and Undefined Behavior in C.int *p = malloc(n*sizeof(*p)); if(p) { ... } free(p); free(p);
Double free. The second
free(p) corrupts the allocator's bookkeeping. Fix: free(p); p = NULL; so a repeat call becomes the harmless free(NULL).v = realloc(v, cap*sizeof(*v));
Self-assign leak. If
realloc fails it returns NULL but keeps the old block alive — you just overwrote its only pointer, leaking it. Use a temporary and commit only on success.char *s = malloc(strlen(t)); strcpy(s, t);
Off-by-one.
strlen excludes the terminating '\0', so the copy writes one byte past the block. You need strlen(t) + 1.int *p = malloc(n*sizeof(*p)); if(p==NULL); return -1; use(p);
Stray semicolon. The
if has an empty body, so return -1 runs unconditionally — and if you removed the semicolon you'd still leak by returning before free. The logic and cleanup are both broken.free(a); a[0] = 5;
Use-after-free. After
free, the block may already be reissued to another allocation; writing to it is Undefined Behavior in C even though the pointer bits look unchanged.int *p = calloc(-1, sizeof(*p));
Negative count wraps.
calloc's parameters are size_t (unsigned), so -1 becomes a huge value; the call almost certainly returns NULL, and if you skip the null check you'll crash on first use.static int *g; void f(){ g = malloc(10*sizeof(*g)); } called twice, no free.
Leak. The second call overwrites
g, dropping the first block's only pointer with no free — a classic leak that Memory Leaks and Valgrind would flag.Why questions
Why write malloc(n*sizeof(*p)) instead of malloc(n*sizeof(int))?
Because
sizeof(*p) tracks p's type automatically. If you later change p from int* to long*, this line stays correct with no edit — it removes a whole class of bugs.Why must you always check malloc's return value?
The heap can be exhausted, in which case
malloc returns NULL; dereferencing NULL is undefined behavior and typically a crash. Checking turns a silent disaster into a controlled failure path.Why does calloc take two arguments instead of one product?
It splits size into
count and size so the library performs the multiplication — giving it the chance to detect overflow of that product and return NULL (on most, though not all, implementations).Why use a temporary variable with realloc?
On failure
realloc returns NULL but the old block is still valid. Assigning into a tmp first preserves the original pointer so you can free it cleanly instead of leaking it.Why set a pointer to NULL right after freeing it?
A freed pointer is dangling — it aims at reclaimed memory. Nulling it turns an accidental reuse into a clean crash instead of silent corruption, and makes any repeat
free a safe no-op.Why can't the stack replace the heap for runtime-sized data?
Stack frames are sized and freed automatically at compile/scope boundaries and have limited size; the heap gives you memory whose amount and lifetime you control at runtime — the whole point per Arrays vs Pointers.
Why does doubling capacity beat growing by a fixed amount?
Each copy costs the current length, but doubling makes copies happen only times over pushes, so total copy work sums to amortized rather than .
Edge cases
What does malloc(0) return?
Implementation-defined: either
NULL or a unique non-null pointer that you may not dereference but must still free. Never assume it's non-null and usable.What happens if you free a pointer that came from the stack (e.g. &x)?
Undefined behavior —
free may only receive a pointer returned by malloc/calloc/realloc (or NULL). Passing anything else corrupts the allocator.Is free(p + 1) valid if p was a valid malloc result?
No. You must free the exact pointer the allocator gave you, not an interior or offset pointer. Freeing a shifted address is undefined behavior.
If realloc moves the block, are your old pointers into it still valid?
No. When
realloc relocates data, every previously saved pointer (and index cached as an address) into the old block becomes dangling. Only the newly returned pointer is valid afterward.Does forgetting free before program exit cause a leak that matters?
Technically the OS reclaims all memory at process exit, so a one-shot leak is harmless — but in long-running or looping code it accumulates and is still a real bug that Memory Leaks and Valgrind will report.
Can you realloc a pointer to a smaller size, and is the surviving data safe?
Yes. Shrinking is allowed; the first
n bytes are preserved and the tail is released. The returned pointer may differ, so still assign it back via a temporary.