Hello! Welcome to your next lesson in the "Pointer Arithmetic and Arrays" module.
In our last session, we explored pointer subtraction, learning how ptr2 - ptr1 gives us the number of elements between two pointers within the same array. This is a vital tool for calculating ranges and buffer sizes. We saw how this operation is implicitly scaled by the size of the data type, a detail crucial for writing portable code.
Today, we'll shift from calculating static distances to dynamic movement. Our goal is to master the technique of traversing arrays using pointers instead of index variables. We will combine our knowledge of pointer arithmetic and array-pointer equivalence to build efficient and idiomatic C loops. This skill is fundamental for manipulating data buffers, implementing algorithms, and understanding a vast amount of existing C code, particularly in embedded systems and communication stacks where direct memory handling is routine.
The "Moving Pointer" Paradigm
You're already comfortable with the equivalence of arr[i] and *(arr + i). While the latter uses pointer arithmetic, it still relies on an index i. True pointer-based traversal involves modifying the pointer itself to "walk" through the array's elements.
The core idea is simple:
- Initialize a pointer to the beginning of the array.
- In a loop, dereference the pointer to access the current element.
- Increment the pointer to move it to the next element.
- Stop when the pointer has passed the last element.
A common mistake is to manually add the size of the data type to the pointer. Let's watch a video that clarifies why this is wrong and what the compiler does for you automatically. The presenter also looks at the resulting assembly code, which should be interesting given your background.
you will never ask about pointer arithmetic after watching this video
This video from the 'Low Level' channel explains the most critical aspect of pointer-based traversal: the compiler automatically scales the increment. It highlights a common pitfall and shows the resulting machine code.
Watch from 02:35 to 04:31 and then from 05:23 to 07:05. Pay close attention to why p_person + 1 is correct, while p_person + sizeof(struct person) is incorrect. Note the add instruction in the assembly dump, which adds the correct byte offset (0x44, or 68 bytes) automatically.
As the video demonstrates, when you write ptr++, the compiler knows the type ptr points to and adds the correct number of bytes (sizeof(*ptr)) to advance the pointer to the next element. This automatic scaling is what makes pointer arithmetic both powerful and clean.
Implementing Pointer-Based Traversal
Let's see how this works in practice with a standard for loop.
Standard Index-Based Loop
Here is a familiar loop that sums the elements of an array using an index:
#define SIZE 5
int array[SIZE] = {10, 20, 30, 40, 50};
int sum = 0;
for (int i = 0; i < SIZE; i++) {
sum += array[i];
}
// sum is now 150
Pointer-Based Loop
Now, let's rewrite this using a moving pointer. A common and robust idiom is to create a second pointer that marks the end of the traversal range.
#define SIZE 5
int array[SIZE] = {10, 20, 30, 40, 50};
int sum = 0;
int *ptr;
int * const end_ptr = array + SIZE; // Pointer to one element PAST the end. A const pointer is good practice.
for (ptr = array; ptr < end_ptr; ptr++) {
sum += *ptr;
}
// sum is again 150
Let's break this down:
int *ptr;: Declares our iterator pointer.int * const end_ptr = array + SIZE;: Declares aconstpointer that points one element past the end of the array. This is a valid address for comparison but must not be dereferenced. Usingconstprevents it from being accidentally modified.ptr = array;: The initialization sets our iteratorptrto point to the first element ofarray.ptr < end_ptr;: The condition checks if our iterator has reached the end. This is a safe comparison.ptr++: The increment movesptrto the next integer in memory.*ptr: Inside the loop, we dereferenceptrto get the value of the element it currently points to.
The following image provides a fantastic visualization, connecting the C code for a pointer-based loop to the memory layout and the underlying assembly instructions.

Practical Demonstration
Let's watch one more short video that walks through traversing both a simple integer array and an array of structs using pointer increments. This will solidify your understanding of how the concept applies universally regardless of the data type.
Master Pointer Arithmetic In 10 Minutes (The Ultimate Guide For C Programmers).
This video from HuwsTube demonstrates pointer arithmetic to traverse an array, clearly showing the pointer value (address) changing with each increment.
Watch from 01:00 to 03:02 and from 08:12 to 09:10. The first part covers a simple integer array, and the second part proves that the exact same logic works for a more complex array of structs, reinforcing the type-scaling principle.
For a comprehensive textual reference on pointer arithmetic and its relationship with arrays, the following resource is excellent.
Chapter 8: Pointers and Memory Allocation
The 'Learning C with Pebble' guide provides a clear, written explanation of pointer arithmetic and the interchangeability of pointer and array notation.
Read the sections 'Pointer Arithmetic', 'Pointers and Arrays', and 'Pointer Arithmetic and Array Indicies'. These sections formalize the concepts we've discussed, showing how *(bigspace + 1) is equivalent to bigspace[1] and how ps++ moves the pointer to the next element regardless of whether it's a short* or long*.
An Alternative: The while Loop
The for loop is explicit and clear, but you will often encounter a more compact while loop pattern, especially when processing C-style strings (which are null-terminated character arrays).
This pattern cleverly combines dereferencing, checking the value, and incrementing the pointer.
char message[] = "Hello";
char *c_ptr = message;
// This loop continues until *c_ptr is the null terminator ('\0'), which evaluates to false.
while (*c_ptr) {
printf("%c ", *c_ptr);
c_ptr++;
}
// Output: H e l l o
This can be made even more compact using the post-increment operator:
char *c_ptr = message;
while (*c_ptr) {
printf("%c ", *c_ptr++); // 1. Use the value *c_ptr, then 2. increment c_ptr
}
This concise style is very common in C and is something we will explore more in the next lesson.
Test your understanding!
You have a function that finds the maximum value in an array using index notation. Rewrite it to use pointer-based traversal. Do not use the [] operator in your new implementation.
Original Function:
int find_max_by_index(int *arr, int size) {
if (size <= 0) return -1; // Or some other error indication
int max_val = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] > max_val) {
max_val = arr[i];
}
}
return max_val;
}
Your Task: Implement find_max_by_pointer
int find_max_by_pointer(int *arr, int size) {
if (size <= 0) return -1;
// Your implementation here using a moving pointer
}
Show answer
Here is one possible implementation:
int find_max_by_pointer(int *arr, int size) {
if (size <= 0) return -1;
int max_val = *arr; // Initialize max_val with the first element's value
int *ptr = arr + 1; // Start the loop from the second element
int * const end_ptr = arr + size;
for (; ptr < end_ptr; ptr++) {
if (*ptr > max_val) {
max_val = *ptr;
}
}
return max_val;
}
Explanation:
- We initialize
max_valby dereferencing the base pointerarr. - The iterator pointer
ptris initialized toarr + 1to start the comparison from the second element. - The loop continues as long as
ptris within the array bounds (ptr < end_ptr). - Inside the loop,
*ptris compared withmax_val, andmax_valis updated if a larger element is found.
Conclusion
In this lesson, we've transitioned from accessing array elements via offsets to actively traversing them with a moving pointer. This is a cornerstone of idiomatic C programming.
Key Takeaways:
- Arrays are traversed by initializing a pointer to the start and incrementing it in a loop.
- The loop condition should compare the iterator pointer against a pointer to one element past the end of the array (e.g.,
ptr < arr + size). - The C compiler automatically handles the scaling of pointer increments (
ptr++), making the code clean and type-independent. - This technique is not just an alternative to indexing; it's a fundamental pattern for low-level data manipulation, especially with strings and raw data buffers.
In our next and final lesson of this module, we will apply these traversal techniques specifically to C strings, learning how to implement standard string library functions from scratch using pointer arithmetic.