0 · Recall Warm-up
Answer from memory first. Answers are in the Answer Vault at the bottom.
- What does
&a + 1skip forint a[5]? from Lesson 1.5.1 - How can a function change two of its caller’s ints? from Lesson 1.4.2
- What is a function prototype? from Lesson 1.3.1
1 · The Hook
🎣
A cinema hall has 10 rows of 20 seats. Seat (3, 5) is easy to describe on a ticket, but memory is a single long line of bytes. So C lays the rows out one after another: all of row 0, then all of row 1, and so on. Finding a seat becomes arithmetic.
2 · Predict First
🤔
int a[10][20]; starts at address 1000, with 4-byte ints. At what address is a[3][5]?
Make a guess in your head. The answer opens Section 3.
3 · Core Concept
💡
Predict First — the answer
1260. Rows 0, 1, 2 come first: 3 × 20 = 60 elements; then 5 more in row 3: 65 elements × 4 bytes = 260 bytes after 1000.
Declaring a 2-D array
int m[3][4]; is an array of 3 rows, each an array of 4 ints. m[i][j] is row i, column j.
int m[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};- Inner braces may be dropped: values fill row by row. Missing values are 0.
- Only the first size may be omitted:
int m[][4] = {…};
int z[3][4] = {{1}, {2, 3}};
printf("%d %d %d %d\n", z[0][0], z[0][1], z[1][1], z[2][3]);
Output
1 0 3 0
Row-major layout
C stores 2-D arrays in row-major order: row 0 entirely, then row 1, and so on. For T a[R][C] starting at B with element size w:
address of a[i][j] = B + (i × C + j) × w
Note that the number of rows R is not needed; the number of columns C is.
Rows are arrays, and decay too
m[i] is row i, an array of 4 ints, which decays to &m[i][0]. m itself decays to a pointer to row 0, a pointer to an array of 4 ints, so m + 1 jumps a whole row (16 bytes). Therefore m[i][j] == *(*(m + i) + j) == *(m[i] + j).
int m[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
printf("%d %d %d\n", m[1][2], *(*(m + 2) + 1), *(m[0] + 3));
printf("%d %d\n", (int)((char *)(m + 1) - (char *)m), (int)((char *)(m[0] + 1) - (char *)m[0]));
Output
7 10 4
16 4
Passing a 2-D array
The parameter must state the column count, so the function can compute row starts: int f(int m[][3], int rows) (equivalently int (*m)[3]).
int sumAll(int m[][3], int rows)
{
int i, j, s = 0;
for (i = 0; i < rows; i++)
for (j = 0; j < 3; j++)
s += m[i][j];
return s;
}
int main(void)
{
int t[2][3] = {{1, 2, 3}, {4, 5, 6}};
printf("%d\n", sumAll(t, 2));
return 0;
}
Output
21
Pointer to array vs array of pointers
| Declaration | Read as | sizeof here |
|---|---|---|
int *p[4]; | p is an array of 4 pointers to int | 16 (4 pointers) |
int (*q)[4]; | q is a pointer to an array of 4 ints | 4 (one pointer) |
int *p[4];
int (*q)[4];
printf("%u %u %u\n", sizeof(p), sizeof(q), sizeof(*q));
Output
16 4 16
A pointer to an array is exactly what a 2-D array decays to; stepping it moves a whole row:
int m[2][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}};
int (*row)[4] = m;
row++;
printf("%d %d\n", (*row)[0], row[0][3]);
Output
5 8
Reading declarations: start at the name, go right, then left
Brackets [] on the right bind tighter than * on the left; parentheses override.
| Declaration | Meaning |
|---|---|
int *p[10]; | p is an array of 10 — pointers to int |
int (*p)[10]; | p is a pointer — to an array of 10 ints |
int **p; | p is a pointer — to a pointer to int |
int *(*p)[5]; | p is a pointer — to an array of 5 — pointers to int |
int *f(int); | f is a function taking int, returning a pointer to int |
4 · Visual
int m[3][4] laid out row-major from 2000: row 0 (orange-free blue cells 0–3), row 1 (4–7), row 2 (8–11). m[1][2] is element 1 × 4 + 2 = 6 → address 2024.
Table view Memory view (row-major, one long line)
col0 col1 col2 col3
row0 [ 1 2 3 4 ] | 1 2 3 4 | 5 6 7 8 | 9 10 11 12 |
row1 [ 5 6 7 8 ] row 0 row 1 row 2
row2 [ 9 10 11 12 ]
m[i][j] = *(*(m + i) + j) address = base + (i * 4 + j) * sizeof(int)
The table is an illusion; memory holds the rows end to end.
5 · Worked Examples
Example 1 Easy
short s[8][6]; starts at 2000 (2-byte shorts). Where is s[4][1]?
Solution
(4 × 6 + 1) × 2 = 50 bytes in: 2050.
Example 2 Medium
double d[5][10]; starts at 500 (8-byte doubles). Where is d[2][7]? Why is the number of rows (5) not needed?
Solution
(2 × 10 + 7) × 8 = 216: 716. Only the elements before d[2][7] matter: complete rows (each 10 wide) and the start of row 2. The total row count only matters for the array’s end.
Example 3 GATE
What does this print?
int a[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = &a[0][0];
printf("%d %d %d %d\n", p[4], *(a[1] + 2), **(a + 2), *(*(a + 1) + 1));
Solution
p walks the 9 ints in order: p[4] = 5. a[1] + 2 points at a[1][2] = 6. a + 2 is row 2, **(a + 2) = a[2][0] = 7. ((a + 1) + 1) = a[1][1] = 5.
Output
5 6 7 5
6 · GATE Traps
⚠️
- Row-major: address uses the number of columns, not rows.
- In a 2-D parameter, every size except the first must be given.
int *p[4](array of pointers) vsint (*p)[4](pointer to array): different sizes and meanings.- m + 1 jumps a whole row; m[0] + 1 jumps one element.
- Missing inner initializers are zero; inner braces decide which row values go to.
8 · Memory Hook
🧠
“Rows are streets laid end to end.” To reach seat (i, j): skip i whole streets of C houses, then walk j houses.
9 · Connect the Dots
Lessons 1.5.1–1.5.2 built one- and two-dimensional arrays and their pointer meanings. The most common array in real programs holds characters: names, messages, file paths. Next lesson answers: how does C store text, and how does a function know where a piece of text ends?
10 · Self-Test
GATE format. Try every question before opening the vault.
Q1NAT1 markAddress
int a[10][20]; starts at 1000 (4-byte ints, row-major). What is the address of a[3][5]?
Numerical answer: ________
Q2MCQ1 markDeclarations
int (*p)[10]; declares p as:
- (A) an array of 10 pointers to int
- (B) a pointer to a pointer to int
- (C) a function returning a pointer
- (D) a pointer to an array of 10 ints
Q3NAT2 markssizeof
With 4-byte ints and pointers, what is sizeof(int *[8]) (an array of 8 pointers to int)?
Numerical answer: ________
Q4MCQ2 marksOutput
What does this print?
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int *p = a[0];
int (*q)[3] = a;
printf("%d %d %d\n", *(p + 4), (*(q + 1))[2], *(*q + 1));
- (A)
5 6 4 - (B)
5 3 2 - (C)
4 6 2 - (D)
5 6 2
Q5MSQ1 mark2-D arrays
For int m[3][4];, which equal m[2][1]?
- (A)
**(m + 2) + 1 - (B)
*(*(m + 2) + 1) - (C)
*(m[2] + 1) - (D)
*(*m + 9)
11 · Rule Card
- T a[R][C]: &a[i][j] = B + (i·C + j)·w (row-major).
- a[i][j] ≡ ((a + i) + j) ≡ *(a[i] + j).
- a decays to T (*)[C]; a + 1 jumps a row; a[i] + 1 jumps an element.
- Parameter: int m[][C] or int (*m)[C] — columns required.
- int *p[N] array of pointers; int (*p)[N] pointer to array.
- Read declarations: name → right ([] , ()) → left (*), parentheses first.
12 · Concept Ledger
Unlocked in this lesson. From here on, these terms may be used.
2-D arrayrow-major orderaddress formula B + (iC + j)wpointer to arrayarray of pointersreading declarations
13 · Answer Vault
Answer Vault
Try the questions first. Open when you are ready to check.
Recall Warm-up
- The whole array: 20 bytes.
- Take pointer parameters and be called with their addresses.
- A declaration of the function’s name and types, written before calls when the body comes later.
Self-Test
Q1 Answer: 12601 mark
(3 × 20 + 5) × 4 = 260 → 1260.
Re-read: Lesson 1.5.2 — 2-D arrays; row-major layout; pointer to array vs array of pointers; reading complex declarations
Q2 Answer: (D)1 mark
Parentheses bind * to p first.
- (A) is wrong: That is int *p[10].
- (B) is wrong: That is int **p.
- (C) is wrong: No function is involved.
Re-read: Lesson 1.5.2 — 2-D arrays; row-major layout; pointer to array vs array of pointers; reading complex declarations
Q3 Answer: 322 marks
8 × 4 = 32.
Re-read: Lesson 1.5.2 — 2-D arrays; row-major layout; pointer to array vs array of pointers; reading complex declarations
Q4 Answer: (D)2 marks
p walks the 6 ints: (p + 4) = 5. q + 1 is row 1: ((q + 1))[2] = 6. *q + 1 points at a[0][1] = 2.
Output
5 6 2
- (A) is wrong: *q + 1 is element a[0][1].
- (B) is wrong: q + 1 is row 1, whose index 2 is 6.
- (C) is wrong: Index 4 from the start is 5.
Re-read: Lesson 1.5.2 — 2-D arrays; row-major layout; pointer to array vs array of pointers; reading complex declarations
Q5 Answer: (B) (C) (D)1 mark
*m + 9 is element 9 counted from the start = 2 × 4 + 1.
- (A) is wrong: That is m[2][0] + 1.
Re-read: Lesson 1.5.2 — 2-D arrays; row-major layout; pointer to array vs array of pointers; reading complex declarations