CPointer Arithmetic

Pointer Arithmetic

Pointers are not just addresses you can read or write through — they support a limited form of arithmetic too. Adding to, subtracting from, or comparing pointers is the mechanism that makes it possible to walk through an array one element at a time using a pointer instead of an index, and it is one of the reasons arrays and pointers feel so closely related in C.

A pointer step is scaled by the pointed-to type

When you write p + 1 for a pointer p, C does not simply add one byte to the address. It adds sizeof(*p) bytes — the size of whatever type p points to. This is what makes p + 1 land exactly on the next element of an array of that type, regardless of how large each element is.

C
#include <stdio.h>

int main(void) {
    int nums[4] = {10, 20, 30, 40};
    int *p = nums;   // points to nums[0]

    printf("p       = %p, *p       = %d\n", (void *)p, *p);
    printf("p + 1   = %p, *(p + 1) = %d\n", (void *)(p + 1), *(p + 1));
    printf("p + 2   = %p, *(p + 2) = %d\n", (void *)(p + 2), *(p + 2));

    return 0;
}
p       = 0x7ffd3a1c2b90, *p       = 10
p + 1   = 0x7ffd3a1c2b94, *(p + 1) = 20
p + 2   = 0x7ffd3a1c2b98, *(p + 2) = 30
4 bytes per step, not 1
Each printed address jumps by 4, the size of an `int` on this platform — not by 1. A `double *` stepping forward would jump by 8 bytes per increment, a `char *` by 1 byte. The compiler works this out automatically from the pointer's declared type; you never have to multiply by `sizeof` yourself when doing pointer arithmetic.
Walking an array with a pointer

Because p + 1 means "the next element," incrementing a pointer in a loop is a common, idiomatic way to visit every element of an array:

C
#include <stdio.h>

int main(void) {
    int nums[5] = {2, 4, 6, 8, 10};
    int *p = nums;

    for (int i = 0; i < 5; i++) {
        printf("%d ", *(p + i));
    }
    printf("\n");

    // Or, advancing the pointer itself:
    for (p = nums; p < nums + 5; p++) {
        printf("%d ", *p);
    }
    printf("\n");

    return 0;
}
2 4 6 8 10
2 4 6 8 10
Pointer subtraction

Subtracting one pointer from another (when both point into the same array) gives the number of elements between them, not the number of bytes:

C
#include <stdio.h>

int main(void) {
    int nums[5] = {2, 4, 6, 8, 10};
    int *start = &nums[0];
    int *end   = &nums[4];

    ptrdiff_t distance = end - start;
    printf("distance = %td elements\n", distance);

    return 0;
}
distance = 4 elements

ptrdiff_t, from <stddef.h>, is the correct signed integer type for the result of a pointer subtraction and is what %td expects.

The one rule that matters most
Only walk inside the array (plus one)
Pointer arithmetic is only well-defined while the resulting pointer stays within the bounds of the array it started in, or points one element past the last element (a valid, non-dereferenceable "end" marker used for loop conditions like `p < end`). Moving further than one-past-the-end, or moving before the first element, is undefined behavior — even if you never dereference the out-of-range pointer. The compiler is free to assume this never happens, which means such code can misbehave in surprising, hard-to-debug ways rather than failing cleanly.

C
int nums[5] = {2, 4, 6, 8, 10};
int *p = nums;

int *ok_end = nums + 5;   // OK: one past the last element, do not dereference
int *bad    = nums + 6;   // undefined behavior: out of bounds
int value   = *(nums - 1); // undefined behavior: before the array starts
  • p + n advances p by n * sizeof(*p) bytes, landing on the n-th element after it.

  • p2 - p1 (both pointing into the same array) gives the element count between them, typed ptrdiff_t.

  • Comparing pointers with <, >, == only makes sense when they point into the same array.

  • Valid pointer arithmetic stays within the array, or reaches exactly one-past-the-end — never further.