Exercises — Arrays and pointers — array name decays to pointer
Assume a typical 64-bit machine for all problems: sizeof(int) == 4, sizeof(char) == 1, sizeof(double) == 8, and every pointer is 8 bytes. All the ideas come from the parent note on decay.

The picture above is the mental model for the whole page: an array name in a value context becomes a red arrow to box 0. Keep it in mind on every question.
Level 1 — Recognition
Can you spot whether decay happens and what the value/type is?
Recall Solution L1.1
The rule: names become arrows except S.A.S. (Sizeof, Ampersand, String-literal-init).
- (a)
a + 1— value context → decays.abecomesint*, soa+1has typeint*. - (b)
sizeof a— the S exception → does NOT decay. It sees the whole typeint[10]. - (c)
&a— the A exception → does NOT decay. Type isint(*)[10]. - (d)
*a—adecays toint*, then*dereferences it → typeint, valuea[0].
Recall Solution L1.2
a(in a value context) :::int*&a[0]:::int*— the&here is on an element, not ona, so no whole-array pointer.&a:::int(*)[5]— pointer to the whole array.a[2]:::int— it's an element, not an address. Noticeaand&a[0]are the same value and the same typeint*. But&ashares the value while having a different type.
Level 2 — Application
Compute concrete sizes and addresses.
Recall Solution L2.1
sizeof(d)= — no decay, whole array.sizeof(p)= —pis a genuine pointer (8 bytes).sizeof(d)/sizeof(d[0])= — this is the famous element-count idiom, valid only wheredis the real array (not decayed).
Recall Solution L2.1... L2.2
Pointer +1 steps by the size of the pointed-to type.
- (a)
a=1000. - (b)
a + 1—aisint*, step = 4 →1004. - (c)
&a + 1—&aisint(*)[6], step = whole array = →1024. - (d)
&a[3]=1000 + 3\times4=1012.
Recall Solution L2.3
sizeof(s)= — declared size, chars are 1 byte each: .s[3]—"hi"fillss[0]='h',s[1]='i',s[2]='\0', ands[3]is the leftover, zero-initialized to'\0'=0.
Level 3 — Analysis
Explain a result, not just compute it.
Recall Solution L3.1
Prints 8. A parameter written int arr[100] (or int arr[], or int arr[999]) is rewritten by the compiler to int *arr — the bracket count is ignored for parameters. At the call, the caller's array already decayed to a pointer, so only the 8-byte address travels in. sizeof(arr) therefore measures a pointer. See Passing Arrays to Functions.
Recall Solution L3.2
By definition x[y] \equiv *(x+y). Addition is commutative, so:
For -1[a+2]: treat x = -1, y = a+2:
So -1[a+2] is just a[1]. (Ugly, legal, and proof that indexing is pure Pointer Arithmetic.)
Recall Solution L3.3
pisint*→p++advances bytes (one int).qisint(*)[5]→q++advances bytes (one whole array). Same starting value, different type ⇒ different stride. The type is the "ruler" pointer arithmetic uses.
Level 4 — Synthesis
Combine decay with multidimensional arrays and function design.

Recall Solution L4.1
A 2D array is an array of arrays: m has type int[3][4], i.e. "3 elements, each an int[4]." Decay peels the outermost dimension only:
a pointer to a row of 4 ints. So m + 1 steps by one row = bytes, landing on m[1]. See Multidimensional Arrays.
Recall Solution L4.2
The blank is the element count: sizeof(v)/sizeof(v[0]) = , so s = sum(v, 5). The result is 2+4+6+8+10 = 30.
The function cannot compute n because inside sum, a is a decayed int* — sizeof(a) would be 8 (pointer), giving the wrong count. The length must be measured where the array is still an array (in the caller) and passed explicitly.
Recall Solution L4.3
Because grid decays to int(*)[4], the parameter must be:
void print_grid(int (*g)[4], int rows) // or equivalently int g[][4]The inner dimension 4 must be given — the compiler needs it to compute g[i] (skip ints) and then [j]. The row count can be a runtime parameter, but the column count is baked into the pointer's type.
Level 5 — Mastery
Predict, prove, and reason about edge cases.
Recall Solution L5.1
*(a + 2)— move 2 ints, then read →a[2]= 30.*a + 2—*aisa[0]=10, then+2(plain int add) → 12. Precedence:*binds before+.2[a]=*(2+a)=a[2]= 30.sizeof a / sizeof *a= = 4 (element count idiom).
Recall Solution L5.2
a + 3(address ofa[3], one past the last element) is legal to form — the C standard specifically permits a "one-past-the-end" pointer for loop bounds likefor (p = a; p < a+3; p++).*(a + 3)is undefined behavior — you may compute that address but you may not dereference it. There is no element there. This is why the boundary is exactlyn, notn-1orn+1, in the loop condition.
Recall Solution L5.3
- (a)
p = a;✅ —adecays toint*, assigned to a pointer. - (b)
a = p;❌ — an array name is not a modifiable lvalue; you cannot make the array point elsewhere. - (c)
p = &a[0];✅ — same value as (a), explicit form. - (d)
a++;❌ —++needs a modifiable lvalue;aisn't one. (Butp++is fine.)
Recall Feynman recap — the whole ladder in one breath
An array name is a row of boxes with a name painted on the front. Say the name in most places and it turns into a red arrow to box 0 (decay). The arrow's type is the ruler: int* steps 4 bytes, int(*)[4] steps a whole row, int(*)[N] steps the whole array. sizeof, &arr, and string-literal init are the three places the name stays a full array and remembers its length. Everywhere else — index, pass, add — it's just pointer arithmetic in disguise.
Connections
- Arrays and pointers — array name decays to pointer (index 5.1.10) — the parent this drills.
- Pointer Arithmetic — the stride rule behind every L2/L3 answer.
- Passing Arrays to Functions — L3.1 and L4.2.
- sizeof Operator — L1/L2 the decay exception.
- Multidimensional Arrays — L4 peeling one dimension.
- Strings in C — L2.3 and the
'\0'edge. - Memory Layout and Addresses — contiguity behind L4.3 and L5.2.