Updated on
Exam Code C Data structures Exam level Tested code
Solved C exam exercise: allocate a dynamic matrix, rotate it 90 degrees, free all-zero rows with free and finish without leaking memory.

An automated warehouse stores the number of units in each shelf slot in a matrix of R rows and C columns. The operator's monitor is mounted in portrait orientation, so the layout must be shown rotated 90 degrees clockwise. The operator does not want to see empty aisles either: after the rotation, every row made up only of zeros must be removed.
The dimensions are known only at run time, so you must use dynamic memory: an array of pointers allocated with malloc and a separate allocation for each row. Static arrays and VLAs are not allowed.
Write a C program that implements at least these functions. int **create_matrix(int rows, int cols) returns NULL if any allocation fails, and in that case leaks no memory. void free_matrix(int **m, int rows) releases a matrix. int **rotate_right(int **m, int rows, int cols) returns a new matrix with C rows and R columns. int compact(int **m, int rows, int cols) frees the rows that contain only zeros, moves the pointers of the remaining rows up while keeping their order, and returns the new number of rows.
Input (standard input): two integers R and C with 0 ≤ R, C ≤ 100, followed by R·C integers between -1000 and 1000, given row by row. A row whose values cancel out (for example a 1 and a -1) is not empty. A row counts as empty only if every value in it is 0.
Output: if R or C is 0, print only the line EMPTY. Otherwise print the line ROTATED C x R followed by the C rows of the rotated matrix. Then print the line COMPACTED K x R, where K is the number of rows that remain, followed by those K rows. Within a row, values are separated by a single space, with no trailing space. If K is 0, nothing is printed after that header.
When the program ends, every block of allocated memory must have been freed exactly once.
Sample input
3 4
5 0 2 0
1 0 0 0
7 0 4 0Expected output
ROTATED 4 x 3
7 1 5
0 0 0
4 0 2
0 0 0
COMPACTED 2 x 3
7 1 5
4 0 2- Allocates the matrix with malloc (an array of pointers plus one row per pointer), checks every allocation and frees what was already allocated if one fails
- 0.75
- Rotates 90 degrees clockwise into a new C×R matrix with correct indices
- 0.75
- Compaction: frees the all-zero rows with free and shifts the pointers without copying data
- 0.5
- Frees every matrix completely at the end, with no double free
- 0.5
Hints
Hint 1 · Where does each element end up after the rotation?
Draw a 2×3 matrix and rotate it by hand. Notice that the first original column, read from bottom to top, becomes the first row of the rotated matrix. Try to write the destination of element m[i][j] in terms of i, j and rows.
Hint 2 · What if the third row allocation fails?
If the malloc for row i returns NULL, rows 0..i-1 and the pointer array already exist. You have to free them before returning NULL. A free_matrix function that takes the number of rows to free also handles this case: just pass it i.
Hint 3 · Do I need to copy numbers to remove rows?
No. Each row is an independent block, and the matrix only stores pointers to those blocks. To remove a row, free its block and move pointers within the array. Use two indices: one that visits every row and one that marks the next free slot. Then work out how many rows free_matrix must release at the end.
Solution
Explained solution
The solution splits the work into small functions. One creates the matrix and handles partial failures, one frees it, one builds the rotated copy and one removes empty rows. Every malloc then has a free that is easy to find, and that pairing is the first thing a grader looks for in this kind of question.
The original matrix is freed as soon as the rotated one exists, because it is no longer needed. At the end only the rotated, compacted matrix is left, and it is freed using its real number of rows.
#include <stdio.h>
#include <stdlib.h>
/* Frees the first 'rows' rows and then the pointer array. */
static void free_matrix(int **m, int rows)
{
if (m == NULL) {
return;
}
for (int i = 0; i < rows; i++) {
free(m[i]);
}
free(m);
}
/* Allocates a rows x cols matrix. On failure it leaks nothing. */
static int **create_matrix(int rows, int cols)
{
int **m = malloc((size_t)rows * sizeof *m);
if (m == NULL) {
return NULL;
}
for (int i = 0; i < rows; i++) {
m[i] = malloc((size_t)cols * sizeof *m[i]);
if (m[i] == NULL) {
free_matrix(m, i); /* only rows 0..i-1 exist */
return NULL;
}
}
return m;
}
/* Returns a new cols x rows matrix: m rotated 90 degrees clockwise. */
static int **rotate_right(int **m, int rows, int cols)
{
int **rot = create_matrix(cols, rows);
if (rot == NULL) {
return NULL;
}
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
rot[j][rows - 1 - i] = m[i][j];
}
}
return rot;
}
static int row_is_empty(const int *row, int n)
{
for (int k = 0; k < n; k++) {
if (row[k] != 0) {
return 0;
}
}
return 1;
}
/* Frees the all-zero rows and moves the remaining pointers up. Returns the rows left. */
static int compact(int **m, int rows, int cols)
{
int k = 0;
for (int i = 0; i < rows; i++) {
if (row_is_empty(m[i], cols)) {
free(m[i]);
} else {
m[k] = m[i];
k++;
}
}
return k;
}
static void print_matrix(int **m, int rows, int cols)
{
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%s%d", j > 0 ? " " : "", m[i][j]);
}
putchar('\n');
}
}
int main(void)
{
int rows, cols;
if (scanf("%d %d", &rows, &cols) != 2 || rows < 0 || cols < 0) {
printf("ERROR\n");
return EXIT_FAILURE;
}
if (rows == 0 || cols == 0) {
printf("EMPTY\n");
return EXIT_SUCCESS;
}
int **m = create_matrix(rows, cols);
if (m == NULL) {
fprintf(stderr, "Out of memory\n");
return EXIT_FAILURE;
}
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (scanf("%d", &m[i][j]) != 1) {
free_matrix(m, rows);
printf("ERROR\n");
return EXIT_FAILURE;
}
}
}
int **rot = rotate_right(m, rows, cols);
free_matrix(m, rows); /* the original is no longer needed */
if (rot == NULL) {
fprintf(stderr, "Out of memory\n");
return EXIT_FAILURE;
}
printf("ROTATED %d x %d\n", cols, rows);
print_matrix(rot, cols, rows);
int k = compact(rot, cols, rows);
printf("COMPACTED %d x %d\n", k, rows);
print_matrix(rot, k, rows);
free_matrix(rot, k); /* empty rows were already freed inside compact */
return EXIT_SUCCESS;
}Allocation. create_matrix first calls malloc for the array of rows pointers, then once per row for cols integers. Writing sizeof *m avoids repeating the type, so the allocation stays correct if the matrix later holds double. The important detail is partial failure. If row i fails, the code calls free_matrix(m, i), which frees exactly the i rows already created and then the array. Passing rows instead would be a bug, because free would be called on uninitialised pointers.
Rotation. In rotate_right, element m[i][j] moves to rot[j][rows - 1 - i]. Original column j becomes row j of the rotated matrix. Because the destination column is rows - 1 - i, the last original row (i = rows - 1) ends up on the left and the first on the right. In the example test, original column 0 holds 5, 1 and 7 from top to bottom, so the first rotated row is 7 1 5. The result has cols rows and rows columns, which is why it is allocated with create_matrix(cols, rows) and not the other way round.
Compaction. compact uses two indices. i visits every row and k marks the next free slot. If row_is_empty returns 1, that block is released with free(m[i]). Otherwise its pointer moves up to m[k]. Not a single integer is copied, only addresses. That is the advantage of independent rows. Relative order is preserved because k never overtakes i. In the example, rows 1 and 3 of the rotated matrix disappear, leaving 7 1 5 and 4 0 2.
The rule is "all zeros", not "sums to zero". The cancelling-values test checks this: rows -1 1 and 1 -1 add up to 0 but are kept, and only 0 0 is removed. In the all-zeros test, by contrast, all three rows vanish. K is 0 and nothing follows the header.
Final release. After compaction the array rot still has room for cols pointers, but only the first k point to live blocks. The rest are stale copies of pointers that were moved up or already freed. That is why the program calls free_matrix(rot, k). Using cols would cause double frees, which is undefined behaviour. The original matrix is freed right after the rotation, which lowers peak memory use. If you want to review how pointers travel into functions, see the solved exercise on pass by value and by reference in C.
Edge cases. When R or C is 0, the program prints EMPTY without allocating anything. This avoids malloc(0), whose result is implementation-defined. With a single 1×4 row, the rotated matrix is a 4×1 column, and compaction removes the only 0. With a 1×1 matrix, the element stays where it is.
Step-by-step trace · generated by running the code
| Step | i | j | value | rot_row | rot_col | Cell |
|---|---|---|---|---|---|---|
| 1 | 0 | 0 | 5 | 0 | 2 | (0,0) |
| 2 | 0 | 1 | 0 | 1 | 2 | (0,1) |
| 3 | 0 | 2 | 2 | 2 | 2 | (0,2) |
| 4 | 0 | 3 | 0 | 3 | 2 | (0,3) |
| 5 | 1 | 0 | 1 | 0 | 1 | (1,0) |
| 6 | 1 | 1 | 0 | 1 | 1 | (1,1) |
| 7 | 1 | 2 | 0 | 2 | 1 | (1,2) |
| 8 | 1 | 3 | 0 | 3 | 1 | (1,3) |
| 9 | 2 | 0 | 7 | 0 | 0 | (2,0) |
| 10 | 2 | 1 | 0 | 1 | 0 | (2,1) |
| 11 | 2 | 2 | 4 | 2 | 0 | (2,2) |
| 12 | 2 | 3 | 0 | 3 | 0 | (2,3) |
Test cases
| Case | Input | Expected output | Actual output | Result |
|---|---|---|---|---|
| Statement example: 3x4 with two empty aisles | 3 4
5 0 2 0
1 0 0 0
7 0 4 0 | ROTATED 4 x 3
7 1 5
0 0 0
4 0 2
0 0 0
COMPACTED 2 x 3
7 1 5
4 0 2 | ROTATED 4 x 3
7 1 5
0 0 0
4 0 2
0 0 0
COMPACTED 2 x 3
7 1 5
4 0 2 | OK |
| Single element | 1 1
9 | ROTATED 1 x 1
9
COMPACTED 1 x 1
9 | ROTATED 1 x 1
9
COMPACTED 1 x 1
9 | OK |
| All zeros: compaction removes everything | 2 3
0 0 0
0 0 0 | ROTATED 3 x 2
0 0
0 0
0 0
COMPACTED 0 x 2 | ROTATED 3 x 2
0 0
0 0
0 0
COMPACTED 0 x 2 | OK |
| Zero dimension | 0 5 | EMPTY | EMPTY | OK |
| One row: the rotated matrix is a column | 1 4
3 0 8 1 | ROTATED 4 x 1
3
0
8
1
COMPACTED 3 x 1
3
8
1 | ROTATED 4 x 1
3
0
8
1
COMPACTED 3 x 1
3
8
1 | OK |
| Cancelling values are not an empty row | 2 3
1 -1 0
-1 1 0 | ROTATED 3 x 2
-1 1
1 -1
0 0
COMPACTED 2 x 2
-1 1
1 -1 | ROTATED 3 x 2
-1 1
1 -1
0 0
COMPACTED 2 x 2
-1 1
1 -1 | OK |
Actual outputs: code compiled with gcc 14.4.0 (C17) and run in an isolated container on 7 October 2026.
Complexity
Time: reading the matrix costs O(R·C). The rotation visits each cell once, O(R·C). Compaction scans each rotated row at most once in full, also O(R·C) in the worst case. Printing costs the same. The total is O(R·C), linear in the number of cells. No solution can do better, because every cell has to be read.
Memory: during the rotation the original and the rotated matrix coexist. The peak is therefore 2·R·C integers plus R + C pointers, that is O(R·C). Freeing the original immediately after rotating keeps that peak from lasting through the printing phase. Compaction allocates nothing: it works inside the same pointer array and only releases memory.
Common mistakes
- Allocating the rotated matrix with
create_matrix(rows, cols)instead ofcreate_matrix(cols, rows). It happens to work for square matrices and writes past the end of the blocks for all the others. - Writing
rot[j][i] = m[i][j]. That is the transpose, not the rotation. The row order still has to be reversed withrows - 1 - i. - Calling
free_matrix(rot, cols)at the end, after compaction. This double-frees rows that were already freed or that appear twice in the array. - On a partial
mallocfailure, returningNULLwithout freeing the rows already allocated, or freeing every row, including the ones that were never allocated. - Treating a row as empty when its sum is 0. This deletes rows such as
-1 1that still record stock. - Freeing only the pointer array with
free(m)and forgetting the rows, or freeing the rows after the array, when their addresses can no longer be read.
Variants
Allocation in a single contiguous block
The professor may ask for all the data to sit in one block, for example to improve cache locality. That takes two allocations: m = malloc(rows * sizeof *m) and m[0] = malloc(rows * cols * sizeof **m). Then, for each i, set m[i] = m[0] + i * cols. Releasing it becomes free(m[0]); free(m);. The drawback is that compaction can no longer free individual rows. It can only reorder the pointers, and the memory of the removed rows is not returned until the end.
Counter-clockwise or 180-degree rotation
For a counter-clockwise rotation the assignment is rot[cols - 1 - j][i] = m[i][j], again into a C×R matrix. For 180 degrees the result is R×C and the assignment is rot[rows - 1 - i][cols - 1 - j] = m[i][j]. Always check the formula against a corner: element m[0][0] must land in the corner you expect. For more practice with matrix operations, see the solved exercise on C matrix sums, transpose and trace.
Produced with AI support and reviewed by the newsroom



Comentarios
Publicar un comentario