CppPointer Arithmetic

Pointer Arithmetic

Because a pointer holds a memory address, you can perform arithmetic on it — but that arithmetic behaves differently from ordinary number arithmetic. Adding 1 to a pointer does not add one byte; it advances the pointer by the size of whatever type it points to. This is what makes pointers so useful for walking through arrays.

Incrementing and decrementing a pointer
When you write p + 1 for a pointer of type T*, the result is the address sizeof(T) bytes further along in memory — not one byte further. The compiler does this scaling automatically based on the pointer's type.

pointer_increment.cpp

CPP
#include <iostream>

int main() {
    int numbers[4] = {10, 20, 30, 40};
    int* p = numbers;   // points to numbers[0]; array decays to a pointer

    std::cout << *p << std::endl;        // 10

    p++;                                   // moves forward by sizeof(int), NOT 1 byte
    std::cout << *p << std::endl;        // 20

    p += 2;
    std::cout << *p << std::endl;        // 40

    p--;
    std::cout << *p << std::endl;        // 30

    return 0;
}
Pointer arithmetic and array decay
As covered in the Arrays chapter, an array used in most expressions decays into a pointer to its first element. This is why numbers and &numbers[0] are interchangeable, and why numbers[i] is really just shorthand for *(numbers + i) — pointer arithmetic followed by a dereference.

array_pointer_equivalence.cpp

CPP
int numbers[4] = {10, 20, 30, 40};

std::cout << numbers[2]     << std::endl;   // 30
std::cout << *(numbers + 2) << std::endl;   // 30 — identical result

// Walking an array using a pointer instead of an index:
for (int* p = numbers; p != numbers + 4; p++) {
    std::cout << *p << " ";
}
Comparing pointers
Pointers into the same array can be compared with <, >, ==, and so on — the comparison reflects which address comes first in memory. This is exactly how the loop above detects the end of the array: numbers + 4 is a valid “one past the end” pointer used purely as a stopping point, never dereferenced.
Pointer arithmetic is only well-defined within an array
Advancing a pointer is only guaranteed to make sense while it stays within the bounds of the same array, or lands exactly one element past its end (a valid, non-dereferenceable position used for comparisons like the loop above). Moving a pointer beyond that, dereferencing a “one past the end” pointer, or doing arithmetic on two pointers that point into different arrays or variables, is undefined behavior — even if it happens to produce a plausible-looking address.

undefined_pointer_arithmetic.cpp

CPP
int arr[4] = {1, 2, 3, 4};
int other = 99;

int* p = arr + 10;      // undefined behavior: far past the array's end
int* q = &other + 5;    // undefined behavior: other isn't an array at all

int* end = arr + 4;     // OK: one-past-the-end, valid to compute and compare
// int bad = *end;      // NOT OK: dereferencing one-past-the-end is UB
Why this is rarely needed in modern code

Manual pointer arithmetic used to be the standard way to iterate over data in C and early C++. Modern C++ provides much safer alternatives that express the same intent without the risk of stepping outside valid memory:

modern_alternatives.cpp

CPP
#include <iostream>
#include <vector>

int main() {
    std::vector<int> numbers = {10, 20, 30, 40};

    // Range-based for loop: no pointer arithmetic at all
    for (int value : numbers) {
        std::cout << value << " ";
    }
    std::cout << std::endl;

    // Iterators: safer, container-aware "pointer-like" objects
    for (auto it = numbers.begin(); it != numbers.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    return 0;
}
  • p + n moves a pointer by n * sizeof(pointed-to type) bytes, not n bytes.

  • Pointer arithmetic is well-defined only within a single array (including one-past-the-end).

  • Prefer range-based for loops and iterators over manual pointer walking in new code.

Coming up
The next chapter puts pointers and references side by side to help you choose the right tool for a given function signature.