A structured lesson workspace with readable content, hands-on examples, and a clean path to completion.
A 2D array in C is an array of arrays. int grid[3][4] is three arrays of four ints, stored contiguously in row-major order. Row 0 occupies bytes 0-15. Row 1 occupies bytes 16-31. Row 2 occupies bytes 32-47. There is no gap between rows. The compiler translates grid[i][j] to *(grid + i * 4 + j) using pointer arithmetic. Understanding this layout is essential for cache performance, for passing 2D arrays to functions, and for interfacing with libraries that expect flat buffers.
#include <stdio.h>int main(void) {int grid[3][4] = {{ 1, 2, 3, 4},{ 5, 6, 7, 8},{ 9, 10, 11, 12}};printf("sizeof(grid): %zu\n", sizeof(grid));printf("sizeof(grid[0]): %zu\n", sizeof(grid[0]));printf("Rows: %zu, Cols: %zu\n",sizeof(grid) / sizeof(grid[0]),sizeof(grid[0]) / sizeof(grid[0][0]));printf("\nGrid contents:\n");for (int i = 0; i < 3; i++) {for (int j = 0; j < 4; j++) {printf("%3d ", grid[i][j]);}printf("\n");}printf("\nMemory layout (flat):\n");int *flat = (int *)grid;for (int i = 0; i < 12; i++) {printf("%3d ", flat[i]);if ((i + 1) % 4 == 0) printf("\n");}return 0;}
The flat view confirms row-major order. The elements are stored row by row, contiguous in memory. Casting to int* and indexing linearly traverses the same bytes. This is why row-wise traversal (inner loop over columns) is cache-friendly: you access consecutive memory addresses. Column-wise traversal jumps by 16 bytes per access, causing cache misses.
#include <stdio.h>#define COLS 4void print_grid(int grid[][COLS], int rows) {for (int i = 0; i < rows; i++) {for (int j = 0; j < COLS; j++) {printf("%3d ", grid[i][j]);}printf("\n");}}int sum_grid(int grid[][COLS], int rows) {int sum = 0;for (int i = 0; i < rows; i++) {for (int j = 0; j < COLS; j++) {sum += grid[i][j];}}return sum;}int main(void) {int data[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};print_grid(data, 3);printf("Sum: %d\n", sum_grid(data, 3));return 0;}
The column count must be specified in the parameter: int grid[][COLS]. The row count is passed separately. This is because the compiler needs the column count to compute the address arithmetic for grid[i][j]. Without it, it cannot determine how many bytes to skip per row. The first dimension can be omitted because it does not affect the element offset calculation.
2D arrays are arrays of arrays, stored contiguously in row-major order. The compiler handles the index arithmetic. You handle the cache-aware traversal and the function parameter declarations. Next: int main() vs int main(void), a subtle declaration difference that matters for standards compliance.
Finish the lesson once you have worked through the material. This awards ★ 30 XP.