Pointer arithmetic in C lets you do math on pointers — adding integers, subtracting pointers, indexing into arrays without explicit [i] syntax. The catch: arithmetic is scaled by the size of the pointer’s referenced type, so ptr + 1 advances by sizeof(*ptr) bytes, not one byte.

This is fundamental to working with C arrays, traversing linked structures, and implementing low-level data structures.

Why pointers exist

Pointers solve the “modify the caller’s value” problem. The naïve swap function:

void swap(int a, int b){
    int tmp = a;
    a = b;
    b = tmp;
}
int x = 1, y = 2;
swap(x, y);    // x and y unchanged

This swaps copies of a and b inside the function. C is call-by-value — the caller’s x and y are never touched. The fix is to pass pointers:

void swap(int *a, int *b){
    int tmp = *a;
    *a = *b;
    *b = tmp;
}
int x = 1, y = 2;
swap(&x, &y);  // now x and y are swapped

Pointer values are also copied, but those copies still point at the original x and y. Dereferencing them (*a) reads or writes the original variables.

Operators

Three operators control pointers:

  • & (address-of) — gives the memory address of a variable. int *p = &total; makes p point to total.
  • * (dereference) — given a pointer, get or set the value at that address. *p = 12; writes 12 into the variable p points at.
  • -> — shortcut for “dereference the pointer and access a field.” p->field is the same as (*p).field. Saves typing when accessing struct fields through pointers.

Example:

int total = 5;
int *nptr = &total;     // nptr points to total
*nptr = 12;             // total is now 12

Pointer indexing

For an array, the array name decays to a pointer to its first element:

int numbers[4];
int *nptr = numbers;    // no & needed; numbers is already a pointer

You can add or subtract integers from the pointer:

*(nptr + 2) = 10;       // sets numbers[2] to 10

The arithmetic is scaled by the size of the pointed-to type. If nptr is int * (and int is 4 bytes), then nptr + 2 advances 8 bytes — to the third element, not the third byte.

This is why numbers[i] and *(numbers + i) are equivalent in C — they both compute “address of element of numbers.”

What pointer arithmetic doesn’t do

  • Multiplication and division of pointers — not legal.
  • Adding two pointers — not legal (what would the result mean?).
  • Pointer arithmetic across array boundaries — legal up to one-past-the-end, undefined beyond. The standard explicitly allows &array[5] on a 5-element array; that’s the canonical “end” pointer used in for (p = array; p != array+5; p++) loops. What’s undefined is dereferencing it (*p at end) or going further (array+6).

Why this matters for data structures

Linked structures (linked lists, trees, graphs) live in scattered memory locations connected by pointers. Manipulating them means dereferencing chains of pointers, allocating new nodes via malloc, and being careful about what’s a pointer and what’s a value. Pointer arithmetic is also how array-based structures (dynamic arrays, heaps) move data around efficiently.

For C structs (the building blocks of complex data structures), see C struct. For runtime memory allocation, see Dynamic memory allocation.