Concepts / Understanding Pointers and Arrays

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.

  • Programming

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.

bfirst elementanext elementnnext elementanext elementnnext elementalast character\0end markerfollowed byfollowed byfollowed byfollowed byfollowed byfollowed by
Where does the terminating zero sit relative to the characters, and how does a pointer 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.

bindex 0, startaindex 1, after one ++pnindex 2aindex 3nindex 4aindex 5\0index 6, end marker++p++p++p++p++p++p
After each ++p, which element is p pointing at, and which element is compared with x on that iteration?

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.

Notes on the code
  • 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.

p is nullpoints to no arrayp at \0points at a real elementreturn 0first line of the functionp != nullptr stilltruethe source conditionstops before looploop continues
How does a NULL pointer differ from a pointer that points at the terminator, and what does each do to the loop?

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
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.

p is nullptr?guard before readingreturn 0no array*p is '\0'?end-marker checkreturn countloop stops here*p equals x?element comparisoncount += 1match found++padvance to next elementyesnoyesnoyesnothennext pass
On each iteration, which check runs first, the element comparison or the end-marker check, and which branch ends the loop?
Notes on the code
  • 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

Worked example

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.

  1. 1
    Start

    p points at index 0, which holds b. count is 0. The character b is not a, so count stays 0.

  2. 2
    Step to index 1

    ++p moves p to a. The element matches x, so count becomes 1.

  3. 3
    Step to index 2

    ++p moves p to n. No match, so count stays 1.

  4. 4
    Step to index 3

    ++p moves p to a. Match, so count becomes 2.

  5. 5
    Step to index 4

    ++p moves p to n. No match, so count stays 2.

  6. 6
    Step to index 5

    ++p moves p to a. Match, so count becomes 3.

  7. 7
    Step 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.

Result

The function returns 3, which matches the three occurrences of a.

bcount 0acount 1ncount 1acount 2ncount 2acount 3\0loop stops, count 3++p++p++p++p++p++p
At which iteration does the running count stop matching the expected count, and what state of p and count causes the divergence?

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 like
    for (; p != nullptr; ++p) on the input banana, with x = 'a'
    Why it fails

    The 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 instead

    Test 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 fails

    A 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 instead

    Keep 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 fails

    The 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 instead

    Stop 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

EASY

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.

Related concepts

More concepts starting with U