Concepts / Pointer Arithmetic Basics

Pointer Arithmetic Basics

count_x walks a zero-terminated array of char by moving a pointer forward with ++p and comparing each element with x.

  • Programming

A Counting Function Without an Index

A pointer can be moved from one element of an array to the next, so a loop can visit every element of an array without using an index. This article studies a short function that counts how many times a character x appears in a character sequence. The function is short enough to read in one pass, but the loop at its center is exactly where many learners go wrong.

Before reading the explanation, look at the function below and keep a guess in mind about what the loop is doing. Ask yourself what the pointer p points to on each pass, and what has to be true for the loop to finish. Hold on to that guess, because the rest of the article tests it.

cpp
Notes on the code
  • The first line handles a pointer that points to no array at all.
  • The loop header has an empty initializer, so p keeps the value it was given.
  • The ++p step moves p to the next char on every pass.
  • The loop condition tests only whether p itself is null.

How a Pointer Walks Through Characters

An array stores its elements one after another, so the elements sit side by side in memory. 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 points to gives the element at that position. The loop in count_x uses this movement to visit each character in turn, from the first one to the last one.

Pointer arithmetic of this kind means the pointer advances by one element of its target type. For a pointer to char, one element is one character, so ++p moves the pointer exactly one character forward. Each pass through the loop body then checks the element the pointer currently refers to and adds one to the count when it matches x.

index 0bindex 1aindex 2nindex 3aindex 4nindex 5aindex 60 (terminator)
After three ++p steps from the start of the array holding banana, which element does the pointer refer to, and how far is it from the start?

In the diagram, the highlighted cell is the element the pointer refers to after three steps. The pointer sits three characters past the start of the array, and the character it reads there is a. The array ends with a zero value at index 6, which is the terminator. The terminator is stored in the same contiguous run as the characters, so the end marker sits directly after the last usable element.

The Zero Terminator and Its Role

Definition

A zero-terminated array marks its end with a zero value. The zero is stored as an element of the array, placed just after the last character that belongs to the sequence. Because the marker is part of the data, a loop that walks the array can detect the end by looking at the element the pointer refers to.

Key point

The stopping condition of a pointer-based loop must match the array's end marker. If the loop stops at the terminator, every real character is visited exactly once and the loop ends cleanly. If the stopping condition checks something else, the loop has no reliable point at which to end.

Tracing the Correct Counting Loop

Now follow the loop step by step. The input is the zero-terminated array holding banana, and x is the character a. The correct loop stops when the pointer reaches the terminator. The table below records the pointer, the element it reads, and the count after each pass.

Worked example

Counting a in banana with a loop that stops at the terminator

Count how many times x = 'a' appears in the zero-terminated array b a n a n a followed by the terminator.

  1. 1
    Start, p at index 0

    p reads 'b'. That is not 'a', so count stays 0. The element is not the terminator, so the loop continues.

  2. 2
    After one ++p, p at index 1

    p reads 'a'. It matches x, so count becomes 1.

  3. 3
    After two ++p, p at index 2

    p reads 'n'. No match, count stays 1.

  4. 4
    After three ++p, p at index 3

    p reads 'a'. It matches, so count becomes 2.

  5. 5
    After four ++p, p at index 4

    p reads 'n'. No match, count stays 2.

  6. 6
    After five ++p, p at index 5

    p reads 'a'. It matches, so count becomes 3.

  7. 7
    After six ++p, p at index 6

    p reads the zero terminator. The stopping condition is met, so the loop ends and the function returns 3.

Result

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

step 1p at index 0, reads b,count 0step 2p at index 1, reads a,count 1step 3p at index 2, reads n,count 1step 4p at index 3, reads a,count 2step 5p at index 4, reads n,count 2step 6p at index 5, reads a,count 3stopp at index 6, reads 0,count 3++p++p++p++p++p++p
On each iteration of the correct loop, what are the value of p, the element it reads, and the count? At which step does the loop stop?

The count changes only on the three passes where the pointer reads a. The loop stops on the seventh position, which holds the terminator, and that is the point where the array's end marker calls for the loop to stop. The result of 3 is correct because the loop visited every real character and then stopped at the marker.

Null Pointer Check Before Any Read

The function accepts a pointer that may hold the null value, which means it points to no array at all. 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, if (p == nullptr) return 0;, returns zero right away when there is no array.

Predict first: if count_x is called with a null pointer, does the loop body ever run? The first line returns 0 before the loop begins, so no element is read. The null check is a separate job from stopping at the terminator. It answers the question of whether there is any array at all, while the terminator check answers where the array ends.

count_x calledp is null?*p is 0?terminator*p is x?count + 1++pnext elementreturn 0return countyesnoyesnoyesno
Which check ends the loop, and what would happen if the check tested something other than the terminating zero at the array's end?

The flowchart separates the two jobs. The null check runs once, before any element is read. The terminator check runs on every pass and decides when the walk ends. A corrected loop combines the pointer test and the terminator test in its condition, as in the version below, which is a generated correction of the source loop.

cpp
Output
count_x("banana", 'a') returns 3
Notes on the code
  • The null check on the first line still runs before anything is read.
  • The loop condition now also tests *p against the zero terminator.
  • Because the terminator test is in the condition, the loop ends at index 6 of banana.

Where the Source Loop Diverges

The loop in the source tests only the pointer against nullptr. Its condition p != nullptr is true for every position of the array, including the terminator, because the pointer itself is not null there. So the source loop does not stop at the zero terminator at all. It keeps moving the pointer past the end marker, into memory that is not part of the array. The count it reports after that point is not a reliable count of the characters in the sequence, so the result can diverge from what you expected.

  • Common mistake

    Treating the null check as the stopping condition

    What it looks like
    for (; p != nullptr; ++p) on the array banana
    Why it fails

    The null check asks whether the pointer points to any array. It says nothing about where the array's characters end, so the loop has no point at which it stops reading characters.

    Do this instead

    Stop the loop on the zero terminator, for example with *p != 0, and keep the null check as a separate test before the loop.

  • Common mistake

    Assuming the terminator is not visited

    What it looks like

    Expecting the count to be 3 for banana because there are three a characters

    Why it fails

    The terminator is an element of the array and the pointer can reach it. A loop that does not test for it will continue past it.

    Do this instead

    Check the element the pointer refers to, so that the loop ends when it reaches the zero value at the end of the array.

  • Common mistake

    Expecting a null pointer to run the loop body

    What it looks like

    Calling count_x with a null pointer and expecting the loop to count zero matches by reading the array

    Why it fails

    A null pointer points to no array, and reading through it would be an error. The function must check for it before reading any element.

    Do this instead

    Keep the if (p == nullptr) return 0; check on the first line so that no element is read when there is no array.

Practice Tracing a Counting Loop

MEDIUM

The zero-terminated array holds the characters a a b a followed by the terminator. Trace the corrected count_x loop with x = 'a'. Write down the pointer position, the element it reads, and the count after each pass. What is the final count, and at which position does the loop stop?

Hints
  • Track the position of p as an index from 0, starting at index 0.
  • The loop stops when *p is the zero terminator, which sits at index 4 in this array.
  • Count only the passes where the element equals a.

Key Takeaways from the Walk

  1. 1
    A pointer walks a zero-terminated char array by moving forward with ++p, and each step advances one character.
  2. 2
    The zero terminator is stored as the last element of the array, and the loop's stopping condition must match it.
  3. 3
    The null check is a separate test that runs before any element is read, and it handles the case where there is no array.
  4. 4
    A loop that tests only the pointer against nullptr does not stop at the terminator, so its count can diverge from the expected result.

At a glance

  • A pointer can walk a zero-terminated array of char one element at a time by moving forward with ++p.
  • The zero terminator marks the end of the array, and the loop's stopping condition must match it.
  • The null check handles a pointer that points to no array and is separate from the terminator check.
  • A loop that tests only the pointer against nullptr does not stop at the terminator, which is where its result can diverge.

Related concepts

More concepts starting with P