Difficulty: Medium
Correct Answer: for (i = 0; i < MAXROW; i++) { free(p[i]); } free(p);
Explanation:
Introduction / Context:
Correctly freeing dynamically allocated memory is just as important as allocating it. When a two dimensional structure is built using a pointer to pointer pattern, each allocation must be paired with a corresponding free call. This question checks whether you understand the lifetime of both the array of row pointers and the rows themselves, and whether you know the correct order in which to release them.
Given Data / Assumptions:
Concept / Approach:
Memory must be freed in the reverse order of allocation so that you never dereference freed pointers. Each p[i] points to a distinct block of MAXCOL ints, so you must call free on each p[i] to release those row blocks. After all row blocks are freed, the array of pointers itself (pointed to by p) can be freed. At that point, there are no remaining valid p[i] entries, so iterating over them after freeing p would be invalid.
Step-by-Step Solution:
Step 1: Recognise that p points to an array of MAXROW pointers, and each p[i] points to an array of MAXCOL ints.
Step 2: To free the row blocks, write a loop for (i = 0; i < MAXROW; i++) free(p[i]);.
Step 3: After this loop, all int arrays have been returned to the heap, but p itself still points to the array of pointers.
Step 4: Call free(p); to release the memory for the pointer array.
Step 5: Optionally set p to NULL after freeing if it will be used later in the function, to avoid accidental dereference.
Verification / Alternative check:
Counting allocations confirms the approach. There is one allocation for p and MAXROW allocations for each row. Therefore there must be MAXROW + 1 free calls, one for each block. If you track memory with a tool such as valgrind in a similar example, you will see that freeing each p[i] and then p eliminates all leaks. Freeing p first would lose the addresses needed to free the rows, demonstrating why order matters.
Why Other Options Are Wrong:
Option B calls free(p) before free(p[i]), which is incorrect because after freeing p, the pointer array is invalid and dereferencing p[i] is undefined behaviour. Option C frees only p[0] and p, leaking the other row allocations. Option D claims that nothing needs to be freed, which is wrong for long running applications and contradicts the design of malloc and free in C, where the programmer is responsible for deallocation.
Common Pitfalls:
Programmers sometimes free only the top level pointer and forget to free nested allocations, leading to memory leaks that may accumulate over time. Another pitfall is double freeing the same pointer or freeing in the wrong order, which can corrupt the heap. Always match each malloc call with exactly one free and free nested allocations from the leaves upwards.
Final Answer:
You must free each row with for (i = 0; i < MAXROW; i++) free(p[i]); and then free the array of pointers itself with free(p); to correctly release all memory.
Discussion & Comments