Understanding Pointers and Arrays
count_x walks a zero-terminated array of char by moving a pointer forward with ++p and comparing each element with x.
Counting Characters Without an Index
A common task is counting how many times a particular character x appears in a sequence of characters. The usual approach uses an index: you step a number from the first position to the last and look up each character. There is another approach that needs no index at all. A pointer that points to the first element of an array can be moved forward, one element at a time, and reading the value it points to gives the character at that position. This article studies a short function, called count_x in the source, that counts occurrences of x this way.
The function is short enough to read in one pass, but its loop is exactly where many learners go wrong. As you work through the article, keep a guess in mind about what the loop is doing, and check that guess against the trace at each stage.
A Zero-Terminated Array in Memory
An array stores its elements one after another, so the elements sit side by side in memory. A zero-terminated array of char is an array whose last meaningful value is followed by a zero value. That zero marks the end of the array. The character written as '\0' is the zero value that serves as the terminator, so reading characters from the start, you know the sequence is finished when you reach it.
In this layout the characters b, a, n, a, n, a occupy the first six positions and the zero terminator occupies the seventh. The terminator is part of the array's storage, not a visible character, but it is the only signal that tells a reader where the text ends. Any function that walks the array has to stop there, which is the central idea of the rest of this article.
Walking Forward With ++p
A pointer that points to the first element can be moved forward to point to the next element. Each step forward moves the pointer past one element, and reading the value the pointer refers to gives the element at that position. Applying ++p advances the pointer by one element of its target type. For a pointer to char, that is one character, so a single ++p moves the pointer from one character to the character that follows it.
Each arrow in the diagram is one ++p. The pointer never skips an element, so a loop that repeats the step and reads the value at each position visits every character in turn, from the first one to the last one. Notice that the step is measured in elements of the target type, not in a fixed count of raw memory units; for char, the element and the character are the same thing.
The Counting Function Under Study
The function takes a pointer to char and a character x, and returns how many times x appears in the array. Its first line handles the case where the pointer points to no array at all. Its loop header is shown below, with an empty initializer, so the pointer keeps the value it was given.
- The first line returns zero right away when there is no array
- The loop header has an empty initializer, so p keeps its starting value
- The loop condition tests only whether the pointer itself is null
- The ++p step at the end of each pass moves p to the next element
- Each pass checks the element p refers to and adds one to count on a match
Read the loop carefully. The body checks the element the pointer currently refers to, and the step moves the pointer forward. The only thing that decides when the loop ends is the condition p != nullptr. That condition tests the pointer, not the character it refers to. Keep this in mind, because it determines where the loop actually stops.
Null Pointer Versus the Terminator
A null pointer is a pointer that points to nothing. Using it to read a character would be an error, so the function checks for it before doing anything else. The first line returns zero immediately when there is no array. The loop condition p != nullptr repeats the same test on every pass. A null pointer and a pointer that points at the '\0' terminator are different things: the first points to no array at all, while the second points at a real element of an array, the element that marks its end.
What do you think happens?
If the caller passes a null pointer to count_x, what does the function return, and does the loop run at all?
Reveal answer
It returns 0 and the loop never runs.
The first line, if (p == nullptr) return 0;, returns zero right away, so control never reaches the loop.
Where the Loop Should Stop
A zero-terminated array has an end marker, so a loop over it should stop when the element it refers to is that zero. The stopping condition must match the array's end marker. The source loop tests the pointer against nullptr instead, and a pointer walking through a char array does not become null when it reaches the terminator. The terminator is an ordinary zero value sitting inside the array, so the test that matters is on the character, not on the pointer.
- The null guard still runs first, before any character is read
- The loop condition checks the character at p against the end marker
- The loop stops when p refers to the terminator, so the terminator is never counted as a match
This corrected version keeps the null guard and separates the two jobs. The null check protects against a pointer that points to nothing. The end-marker check ends the walk at the terminator. The source loop merged these concerns into a single test on the pointer, which is why it does not stop where the array does.
Tracing a Count Step by Step
Counting a in banana
The array holds the characters b, a, n, a, n, a followed by the terminator. Count how many times x = 'a' appears, using the corrected loop that stops at the terminator.
- 1Start
p points at index 0, which holds b. count is 0. The character b is not a, so count stays 0.
- 2Step to index 1
++p moves p to a. The element matches x, so count becomes 1.
- 3Step to index 2
++p moves p to n. No match, so count stays 1.
- 4Step to index 3
++p moves p to a. Match, so count becomes 2.
- 5Step to index 4
++p moves p to n. No match, so count stays 2.
- 6Step to index 5
++p moves p to a. Match, so count becomes 3.
- 7Step to index 6
++p moves p to the terminator. The check *p != '\0' is now false, so the loop stops before any comparison with x.
The function returns 3, which matches the three occurrences of a.
The trace shows the divergence point clearly. With the corrected condition, the count and the position of p agree at every step and the loop halts on the terminator with count equal to 3. With the source condition p != nullptr, nothing changes until p moves past the terminator. The loop would not stop at index 6, because p is still not null there. It would keep advancing into memory beyond the array, and the source does not describe what those reads would return. The count at the moment the terminator is reached is still 3, but the loop has not finished its job, and its result can no longer be trusted.
Mistakes With the Stopping Condition
Common mistake Assuming the loop stops at the terminator because the array ends there
What it looks likefor (; p != nullptr; ++p) on the input banana, with x = 'a'Why it failsThe condition tests the pointer, and the pointer is not null when it reaches the zero terminator. The array's end is not a condition the loop checks.
Do this insteadTest the element the pointer refers to, for example *p != '\0', so the loop stops where the end marker is.
Common mistake Treating a null pointer and a pointer to the terminator as the same thing
Why it failsA null pointer points to no array, while the terminator is a real element at the end of an array. The guard handles the first; the end-marker check handles the second.
Do this insteadKeep the null guard at the top and a separate end-marker check inside the loop.
Common mistake Counting the terminator as a match
Why it failsThe terminator is a zero value, not a character x. If the loop reads it as an element before stopping, it wrongly treats the end marker as data.
Do this insteadStop the loop before the comparison with x runs on the terminator.
Common mistake Forgetting that ++p moves the pointer by one element, so the loop body must read the element at the new position on the next pass
Tracing a Different Input
Using the corrected loop, trace count_x on the zero-terminated array a, a, followed by the terminator, with x = 'a'. At which index does the loop stop, and what does the function return?
Hints
- Write down the index and the character at each position before the loop starts.
- Ask at each step whether the element p refers to is the terminator before you compare it with x.
- The loop stops on the terminator, so the terminator is never counted.
Key Takeaways
- A zero-terminated char array ends with a zero value, and a pointer can walk it one element at a time with ++p.
- The null check at the top of the function handles a pointer that points to no array, which is a separate job from stopping at the terminator.
- The source loop tests the pointer against nullptr, so it does not stop at the terminator and must be corrected to test the element it refers to.
- Tracing the loop step by step, with the count and the position of p recorded at each iteration, reveals exactly where a wrong stopping condition makes the result diverge.