C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
36: Multidimensional Arrays
I remember a junior dev I worked with a few years ago who was building a simple roguelike game. He tried to store the game map in a single-dimensional array to "keep it efficient." Every time he wanted to check if a player moved north or east, he had to manually calculate the offset: map[y * map_width + x]. He spent an entire afternoon debugging a frustrating segmentation fault, only to realize he'd swapped x and y in one single function call. That's when I sat him down and told him, "Stop doing the coordinate math yourself; let the compiler do it."
Mapping Data to Grids
In C, a multidimensional array is essentially an array of arrays. When you declare int grid[3][4], you aren't actually creating a physical "square" in your computer's RAM—because RAM is fundamentally linear—but you are telling the compiler to treat the data as a table with 3 rows and 4 columns. This makes your code significantly more readable because you can access elements using coordinates that actually make sense to a human.
Here is how you'd actually set one up for something like a small game board:
int game_board[3][3] = {
{1, 2, 3}, // Row 0
{4, 5, 6}, // Row 1
{7, 8, 9} // Row 2
};
To get the value 6 from that board, you just call game_board[1][2]. The first index is the row, and the second is the column. It's a simple mental shift: array[vertical][horizontal]. If you get these backwards, you'll find yourself chasing bugs that feel like ghosts in the machine.
The Linear Reality of Memory
Even though we visualize these as grids, C stores them in row-major order. This means the entire first row is laid out in memory, followed immediately by the second row, and so on. If you were to point a pointer at the start of a 2D array and just increment it, you'd visit every element in the first row before ever hitting the first element of the second row.
This is a critical detail for performance. If you write a nested loop to process a 2D array, you should almost always iterate through the columns (the inner loop) before the rows (the outer loop). This is called "cache locality." If you jump across rows inside your inner loop, you're forcing the CPU to fetch data from different parts of memory constantly, which will slow your program down significantly.
// Do this for better performance:
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
sum += grid[i][j]; // Moving linearly through memory
}
}
One last thing to keep in mind: when you pass a multidimensional array to a function, you must specify the size of the second dimension (and any subsequent dimensions). The compiler needs to know exactly how many elements are in a row so it can calculate the memory offset for the indices you provide. You can leave the first dimension empty, but the rest are non-negotiable: void process_map(int map[][4]) is valid; void process_map(int map[][]) will make the compiler throw a fit.
📋 Practical Task
Exercise: Cinema Seat Reservation Grid
You need to build a simple seat management system for a small boutique cinema. The cinema has 5 rows and 8 seats per row.
- Create a 2D array of integers called
cinema_seatswhere0represents an available seat and1represents a reserved seat. - Initialize the grid so that a few seats are already taken (e.g., the middle seats of the front row).
- Write a function called
book_seatthat takes the row and column as arguments. The function should check if the seat is available; if it is, mark it as reserved and return 1. If it's already taken, return 0. - In your
mainfunction, attempt to book a seat that is already taken and one that is empty, printing a success or failure message for each attempt. - Finally, print the entire grid to the console using nested loops so you can visualize the seating chart.
There are no comments for now.