Pointers and Arrays
Pointer arithmetic, why a[i] is *(a+i), array decay in function calls, and the limits the standard puts on arithmetic.
The Secret Identity of Arrays
Three chapters of IOUs come due at once. Why does scanf("%19s", name) take no &? Why do arrays pass to functions without copying? Why did %s walk memory so naturally? One rule answers all of it: in almost every expression, an array converts to a pointer to its first element. The rule is called decay, and with it, arrays and pointers snap together into one picture.
Decay, and What a[i] Really Is
int *p = values compiles with no & because values in that expression already is a pointer to values[0]: decay in action. Then the equivalence that explains indexing itself: a[i] is defined as *(a + i), take the start, move i elements, dereference. The bracket syntax you have used since chapter 7 was pointer arithmetic all along, which is why p[2] works on a plain pointer, and why (a famous exam curiosity) 2[values] is legal: addition commutes.
Pointer arithmetic scales by the element type. p + 1 does not add one byte; it adds one int, four bytes here, because p is an int *. The type declared in chapter 2, the contiguous layout from chapter 7, and the typed pointers from last lesson combine into: address arithmetic in units of elements.
Walking with a Pointer
The chapter 7 index walk has a pointer twin:
end points one past the last element, and the standard explicitly blesses forming that address (dereferencing it remains out of bounds, the chapter 7 rule in pointer clothes). The p < end walk is idiomatic C, the shape %s and strlen use inside, and comparing or subtracting pointers is defined exactly when both point into the same array. Index walks and pointer walks compile to the same machine code on any modern compiler; the course uses whichever reads clearer, and you must read both fluently, because exams print both.
Arrays in Function Calls: The Whole Truth
Now the chapter 9 and 10 mysteries resolve in one stroke. When you pass an array to a function, decay happens at the call: the function receives a pointer to the first element, never a copy. That is why array arguments take no &, why passing a million-int array is as cheap as passing one address, and why void f(int arr[]) and void f(int *arr) declare the same function, the [] in a parameter list being pure notation.
Two consequences, both load-bearing. First, the function can modify the caller's elements, because it holds their real addresses: your array-filling loops have been out-parameters all along. Second, the function cannot know the array's length: it holds one address, not the object, so sizeof(arr) inside gives the size of a pointer, the classic trap, and the length travels as a separate parameter:
The pointer-plus-count pair is C's array-passing convention, visible in every real API. And structs? They copy whole even with arrays inside (chapter 10's puzzle) because decay applies to array expressions, and a struct argument is a struct expression: its arrays are members of the copied value, not expressions decaying on their own. Both halves of the contradiction were telling the truth.
The Exceptions to Decay
The rule says almost every expression, and the exceptions are exactly two you have met: sizeof(values) measures the whole array (chapter 7's element-count idiom depends on it), and &values takes the address of the entire array object. Everywhere else, string literals initializing arrays aside, array names are pointers-in-waiting. The sizeof exception working only in the declaring scope, and turning into pointer-size inside functions, is the sharpest version of the trap, and exams sharpen it further.
Key Takeaways
- Decay: an array expression becomes a pointer to its first element;
a[i]is*(a + i), and indexing works on any pointer. - Pointer arithmetic moves in elements, not bytes; one-past-the-end may be formed and compared, never dereferenced.
- The
p < endpointer walk is idiomatic; read it and the index walk interchangeably. - Functions receive arrays as pointers: no copy, no
&, caller elements modifiable, length passed separately;int arr[]andint *arrare the same parameter. sizeofinside a function measures the pointer, not the array: the trap that makes the separate count parameter mandatory.- Exceptions to decay:
sizeofand&on the array name, in the declaring scope.
How did you find this lesson?
Your rating helps us improve the content.
Create an account to track your progress and access interactive exercises. Already have one? Sign in.
Pointers and Arrays - Quiz
Test your understanding of the lesson.
Practice Exercises
Sum with a Pointer Walk
Write int sumArray(int *values, int count) that totals the elements, then a main that reads a count and that many integers into an array and reports the function's sum. The array passes as a pointer plus a separate count: C's array-passing convention.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!