5.1.10 · D5C Programming
Question bank — Arrays and pointers — array name decays to pointer
This bank drills the ideas from the parent note. For the mechanics behind the answers see Pointer Arithmetic, Passing Arrays to Functions, sizeof Operator, and Memory Layout and Addresses.
True or false — justify
An array name and a pointer have the same type.
False — an array has type
T[N]; it only decays to T* in value contexts. sizeof and & reveal the difference immediately.int a[10]; then a = p; is legal if p is int*.
False — the array name is not a modifiable lvalue, so you cannot assign to it. Only
p = a; (pointer gets the base address) is allowed.sizeof(a) and sizeof(&a[0]) give the same number for int a[10].
False —
sizeof(a) is 40 (whole array, no decay), while &a[0] is a genuine pointer, so its sizeof is the pointer width (e.g. 8).arr and &arr always print the same address.
True in value — both point at the same first byte — but their types differ (
int* vs int(*)[N]), which changes how +1 moves them.Inside void f(int arr[10]), the 10 guarantees the function knows the length.
False — the compiler ignores that number and rewrites the parameter to
int *arr. The 10 is pure documentation; sizeof(arr) still yields pointer size.3[arr] is a compiler error.
False — since
arr[i] means *(arr+i) and addition commutes, 3[arr] means *(3+arr), which equals arr[3].Passing an array to a function copies all its elements.
False — the name decays to an address, so only that address (4/8 bytes) is copied. The function edits the original boxes.
For int m[3][4];, the name m decays to int**.
False — decay peels off only the outermost dimension, giving
int(*)[4] (pointer to a row of 4 ints), not a pointer-to-pointer. See Multidimensional Arrays.A string literal like "hi" always decays to a char*.
False — when it initializes a
char[] (e.g. char s[] = "hi";) it stays an array and copies its bytes into s. Elsewhere it decays.Spot the error
void f(int a[]) { for(int i=0;i<sizeof(a)/sizeof(a[0]);i++) ... }
a is really int*, so sizeof(a) is pointer size, not array bytes — the loop count is wrong. Pass the length as a separate parameter instead. See Passing Arrays to Functions.int a[5]; int **pp = &a;
&a has type int(*)[5], not int**. The correct declaration is int (*pp)[5] = &a;.int a[5]; int *p = a; if (sizeof(p) == sizeof(a)) puts("same");
Never prints —
sizeof(p) is pointer width, sizeof(a) is 5*sizeof(int). They are only equal by coincidence if int happened to be pointer-sized, which the standard does not promise.char *s = "hello"; s[0] = 'H';
s points into a read-only string literal; writing through it is undefined behaviour. Use char s[] = "hello"; to get a modifiable copy. See Strings in C.int a[3]; int *p = &a;
Type mismatch:
&a is int(*)[3], but p is int*. Use int *p = a; (decay) or int *p = &a[0]; for the first element.return arr; from a function whose local was int arr[10];
The array decays to a pointer to a local that dies when the function returns — you return a dangling address. Return via a caller-supplied buffer or dynamic memory instead.
Why questions
Why does arr+1 move by 4 bytes but (&arr)+1 move by 20 bytes for int arr[5]?
Pointer arithmetic scales by the pointed-to type's size:
arr decays to int* (step 4 bytes) while &arr is int(*)[5] (step = one whole 20-byte array). See Pointer Arithmetic.Why did C's designers make arrays decay at all?
To avoid copying huge arrays on every function call — passing a cheap address gives effective pass-by-reference without a reference type.
Why is sizeof an exception to decay?
sizeof needs the whole object's size, so it must see the real array type T[N]; letting it decay would defeat its purpose and always report pointer size.Why does &arr not decay even though it involves the array name?
& explicitly asks for the address of the whole object, so the compiler keeps the full array type to build T(*)[N]. Decay only fires in plain value contexts, not under & or sizeof.Why is arr[i] legal even though arr "isn't a pointer"?
In
arr[i] the name is used as a value, so it decays to a pointer right there; indexing is then defined as *(arr+i) on that decayed pointer.Why can a pointer be reassigned but an array name cannot?
A pointer is a variable holding an address (an lvalue you can overwrite); an array name is a fixed label for a block of storage, not a slot that can point somewhere new.
Edge cases
What is sizeof(a) for int a[0]?
Zero-length arrays are not standard C; if a compiler extension allows them,
sizeof reports 0 bytes. Portable code should avoid them entirely.For char c[1] = "";, what is sizeof(c)?
1 — the single byte holds the terminating
'\0', and sizeof (no decay) counts the whole array.Does arr decay when it is the operand of sizeof arr[0]?
arr[0] here is just one element of type T, so no array-of-arrays decay matters; sizeof yields sizeof(T). The full array's decay never enters this expression.For a 1-element array int a[1];, is a the same as &a[0]?
Their values match (both address the sole element), but
a has type int[1] and &a[0] has type int*; decay bridges them in value contexts only.When you pass int m[3][4] to a function, what must the parameter look like?
int m[][4] or int (*m)[4] — the outer dimension decays to a pointer-to-row, so the column count 4 must stay to keep arithmetic correct. See Multidimensional Arrays.Is the decayed pointer from an array an lvalue you can take & of?
No — decay produces a temporary pointer value, not a stored pointer object, so
&(arr) is &arr (the whole-array address), not the address of some pointer variable.Recall One-line summary to carry away
The whole bank reduces to: decay is a conversion, not an identity, and it is switched off only by sizeof, &arr, and char[] literal-init.
Connections
- Pointer Arithmetic — why
+1steps differ by type. - Passing Arrays to Functions — the lost-length trap.
- sizeof Operator — the headline exception.
- Multidimensional Arrays — decay peels one dimension.
- Strings in C — literal vs
char[]traps. - Memory Layout and Addresses — the contiguous boxes under it all.