Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
Initializing 2D Arrays in C
Flat lists, braced rows, partial fills, omitted dimensions and designated initializers, all explained with output you can verify. A free C tutorial for embedded C programming.
Initializing 2D arrays in C looks simple until a missing value or a misplaced brace quietly shifts your data into the wrong row. In this lecture of our free C programming course you will learn every initialization style, see exactly what each one produces, and discover which style is safest in real firmware. Getting this right matters for C programming for embedded systems, where constant tables such as key maps, gamma curves and calibration grids are written once and trusted forever.
What You Will Learn
- How a flat list of values fills a 2D array row by row
- How inner braces assign values to individual rows
- What happens to elements you do not mention (they become zero)
- How to let the compiler count the rows for you
- Designated initializers and zero-filling a whole table
- Storing a read-only keypad map with
const
Prerequisites
- Declaring and addressing 2D arrays, plus the row-major memory layout
- Initializing one-dimensional arrays with a brace list
- GCC, so that you can see the compiler warnings first-hand
Initializing 2D Arrays in C: The Two Core Rules
Two rules explain every initialization form you will meet. First, values are consumed in memory order, which for a 2D array means row 0 left to right, then row 1, and so on. Second, any element without a value receives zero, the same rule that applies to a partially filled 1D array. Everything else is a matter of how you group the values with braces.
Initialization Styles Compared
| Style | Example | Result |
|---|---|---|
| Flat list | int a[3][3] = {1, 2, 3, 4}; | Fills row 0, then starts row 1; rest zero |
| Braced rows | int b[3][3] = {{1}, {2, 3}, {4, 5, 6}}; | Each brace group is one row; gaps become zero |
| Open row count | int c[][3] = {{7, 8, 9}, {1, 2, 3}}; | Compiler counts 2 rows |
| Designated | int d[2][3] = {[0][1] = 5, [1][2] = 9}; | Only the named elements are set; rest zero |
| All zero | int e[2][3] = {0}; | Every element is zero |
Free C Tutorial: See Every Style in Action
The program below creates one array per style, prints each with a helper function, and then looks up two keys in a constant 4 by 4 keypad map. Notice how the helper declares int m[][3], using the column-count rule from the previous lecture.
#include <stdio.h>
static void dump(const char *name, int rows, int m[][3])
{
printf("%s:\n", name);
for (int r = 0; r < rows; r++) {
printf(" %d %d %d\n", m[r][0], m[r][1], m[r][2]);
}
}
static const char keymap[4][4] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
int main(void)
{
int flat[3][3] = {1, 2, 3, 4};
int braced[3][3] = {{1}, {2, 3}, {4, 5, 6}};
int open_rows[][3] = {{7, 8, 9}, {1, 2, 3}};
int sparse[2][3] = {[0][1] = 5, [1][2] = 9};
int zeros[2][3] = {0};
dump("flat {1,2,3,4}", 3, flat);
dump("braced", 3, braced);
dump("open_rows", (int)(sizeof open_rows / sizeof open_rows[0]), open_rows);
dump("sparse", 2, sparse);
dump("zeros", 2, zeros);
printf("key at row 2, col 3 = %c\n", keymap[2][3]);
printf("key at row 3, col 2 = %c\n", keymap[3][2]);
return 0;
}
gcc -std=c17 -Wall -Wextra -Wpedantic -o init_demo init_demo.c
./init_demo
GCC reports one warning, on the flat list, which we discuss below. The program then prints:
flat {1,2,3,4}:
1 2 3
4 0 0
0 0 0
braced:
1 0 0
2 3 0
4 5 6
open_rows:
7 8 9
1 2 3
sparse:
0 5 0
0 0 9
zeros:
0 0 0
0 0 0
key at row 2, col 3 = C
key at row 3, col 2 = #
Reading the Results
- flat: four values flow into row 0 (three of them) and the first slot of row 1. Everything left over is zero. This is legal C but easy to misread.
- braced: every inner brace pair names a row explicitly. Row 0 gets just
1, so its other two slots are zero. This style cannot shift data between rows. - open_rows: leaving the first bracket empty lets the compiler count the brace groups. The program recovers the count with
sizeof open_rows / sizeof open_rows[0], which gives 2. - sparse: designated initializers (a C99 feature) set only the named positions, which is ideal for large tables with a few non-zero entries.
- zeros: a single
0initializes the first element explicitly and zero-fills the rest. - keymap: row 2 column 3 is the letter C; row 3 column 2 is the hash key. A row and column from a keypad scan can index this table directly.
The Missing-Braces Warning
With -Wall, GCC flags the flat form:
warning: missing braces around initializer [-Wmissing-braces]
Treat this warning as a hint to add braces. The flat list is valid, but one forgotten value shifts every later element into the wrong row without any error. Braced rows make each row’s contents obvious and turn a missing value into a localized, visible gap.
Constant Tables for Embedded C Programming
The keypad map in the demo is declared static const. On most microcontroller toolchains, a const array with constant initializers is placed in flash memory rather than RAM, which is a real saving on devices with a few kilobytes of RAM. Typical candidates are key maps, sine and gamma tables, segment patterns for seven-segment displays and two-axis calibration grids.
| Table | Dimensions | Why const helps |
|---|---|---|
| 4 by 4 keypad map | 4 rows by 4 columns of char | 16 bytes of RAM saved, accidental edits blocked |
| Calibration grid | Temperature by voltage steps | Fixed at factory, never changes |
| Font glyph rows | Characters by byte columns | Large and read-only, belongs in flash |
Common Mistakes and Troubleshooting
| Mistake | Symptom | Fix |
|---|---|---|
Leaving out the column size: int m[][] = {...} | Error: incomplete element type | Only the first dimension may be omitted |
| Giving more values than the array holds | Warning: excess elements in array initializer | Count rows times columns, or let the compiler size the rows |
| Assuming uninitialized local arrays are zero | Random garbage values | Initialize with = {0} explicitly |
| Trusting a flat list in a long table | One typo shifts all later rows | Use braced rows |
| Brace group with too many values for one row | Compile warning or error | Each row group has at most COLS values |
Local versus static arrays
A local array without an initializer holds indeterminate values, while a global or static array without an initializer is zeroed before main starts. A partial initializer always zero-fills the rest, whichever storage class you use.
Best Practices, Performance and Security
Best Practices
- Use braced rows for any table with more than one row.
- Align values in columns in your source code so errors are easy to spot.
- Mark tables that never change as
static const. - Use designated initializers for large, mostly-zero tables.
Performance Considerations
Constant tables initialized at compile time cost no run-time code at all; the data is simply part of the program image. Large initialized non-const arrays, by contrast, are copied from flash to RAM during start-up, which uses both memory and boot time.
Security Considerations
An uninitialized array can leak stale stack contents if you transmit it. Always initialize buffers that will be sent over a network or radio link, even if you plan to overwrite most of the bytes.
Summary and Key Takeaways
Remember these points
- Values fill the array in memory order; unmentioned elements become zero.
- Braced rows make each row explicit and avoid silent shifting.
- Only the first dimension can be left empty; the compiler then counts the rows.
- Designated initializers set chosen cells;
{0}zero-fills everything. - Use
static constfor lookup tables that should live in flash.
You now know every common way to initialize a two dimensional array, what each one produces, and which style protects you from silent mistakes. These techniques complete the arrays chapter: one-dimensional arrays, arrays in functions, and grids. The next file in this free embedded C course tests everything you have learned with a quiz, output-prediction puzzles, coding challenges and interview questions.
Frequently Asked Questions
How do you initialize a 2D array in C?
Use a brace list, preferably with an inner brace group per row, such as int m[2][3] = {{1, 2, 3}, {4, 5, 6}};.
What happens if I provide fewer values than the array size?
The remaining elements are set to zero. This holds for both flat lists and braced rows.
Can I omit the number of rows when initializing?
Yes. The first dimension may be left empty and the compiler will count the rows from your brace groups, but the column count is mandatory.
Why does GCC warn about missing braces?
You used a flat list for a 2D array. It is valid C, but braces per row prevent silent data shifting and make the layout clear.
How do I set every element of a 2D array to zero?
Write = {0} in the declaration. The first element is set to zero explicitly and the compiler zero-fills the remainder.
What are designated initializers?
A C99 feature that lets you name the index being set, for example [1][2] = 9. Elements you do not name are zero.
Will a const 2D array use RAM on a microcontroller?
On most embedded toolchains a const table with constant initializers is placed in flash, not RAM, but check your linker map to confirm for your target.
Test Yourself in the Next Lecture
Rebuild the initialization demo, predict each printout, then take on the quiz and interview questions that close the arrays chapter of our free C programming course.
Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.

2 Comments