Skip to content

Pointer Arithmetic in C

Updated 7 min read Aimerscrew

In the previous lesson, a pointer held the address of one variable. But pointers can also move. Pointer arithmetic in C lets you add to a pointer, subtract from it and compare it, so it can walk through the elements of an array one by one. In this lesson, you'll learn exactly how far a pointer moves, why, and which operations are allowed.

🌍 Real-life example

Picture a parking lot with numbered spots in a row. You're standing at spot 1. Someone says "move 3 spots forward", and you walk to spot 4. You don't count in meters or feet; you count in spots, whatever size a spot is. Pointer arithmetic works the same way: p + 3 means "3 elements forward", and C works out how many bytes that is for you.

What is pointer arithmetic in C?

Pointer arithmetic is doing math on addresses. C allows a small set of operations on pointers:

  • add a whole number: p + 2, p++, p += 3
  • subtract a whole number: p - 1, p--
  • subtract one pointer from another: end - start
  • compare two pointers: p < end, p == q

All of these only make sense when the pointer points into an array, because array elements sit next to each other in memory with no gaps.

Moving to the next element with p + 1

Let's point at the first element of an array and look at its neighbors:

#include <stdio.h>

int main(void) {
    int scores[5] = {70, 85, 90, 65, 100};
    int *p = &scores[0];   /* point at the first score */

    printf("*p       = %d\n", *p);
    printf("*(p + 1) = %d\n", *(p + 1));
    printf("*(p + 3) = %d\n", *(p + 3));
    return 0;
}
*p       = 70
*(p + 1) = 85
*(p + 3) = 65

p + 1 is the address of the next element, scores[1]. p + 3 is the address of scores[3]. Notice that p itself didn't move: p + 3 calculates a new address, just like x + 3 doesn't change x.

Why does p + 1 move 4 bytes, not 1?

This is the most important rule of pointer arithmetic: adding 1 to a pointer moves it by the size of the type it points to. An int is usually 4 bytes, so an int * jumps 4 bytes at a time. If C moved only 1 byte, you'd land in the middle of an int and read garbage.

The formula is: address of (p + n) = address in p + n × sizeof(*p).

#include <stdio.h>

int main(void) {
    int scores[3] = {70, 85, 90};
    int *p = &scores[0];

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

The exact addresses differ on every computer, but the gaps between them don't. With 4-byte ints you might see 0x7ffc1a2b3c40, then ...3c44, then ...3c48: each step is 4 bytes. The animation below shows the same idea with example addresses.

Animation: a pointer p moves along five int boxes holding 10, 20, 30, 40 and 50 at example addresses 0x1000, 0x1004, 0x1008, 0x100C and 0x1010; each p++ moves it 4 bytes to the next element
Each p++ moves one element forward, which is 4 bytes for an int (addresses are examples).

The step size depends on the type, as this table shows (typical sizes on 64-bit systems):

Pointer typesizeof the targetp + 1 movesp + 3 moves
char *11 byte3 bytes
int *44 bytes12 bytes
double *88 bytes24 bytes

Walking through an array with p++

Because p++ moves to the next element, you can visit a whole array by moving the pointer instead of counting an index. Here, 1 means a parking spot is taken and 0 means it's free:

#include <stdio.h>

int main(void) {
    int spots[5] = {1, 0, 1, 1, 0};
    int *p = &spots[0];

    for (int spot = 1; spot <= 5; spot++) {
        printf("Spot %d: %s\n", spot, *p == 1 ? "taken" : "free");
        p++;   /* walk to the next spot */
    }
    return 0;
}
Spot 1: taken
Spot 2: free
Spot 3: taken
Spot 4: taken
Spot 5: free

Unlike p + 1, p++ really changes p: after each loop, the pointer holds the address of the next element.

📝 Note

You'll often see *p++ in C code, like total += *p++;. It means "use the value p points to, then move p forward". The ++ applies to the pointer, not the value, and because it's postfix, the old address is used first. If that feels tricky, write two lines instead: total += *p; then p++;.

*p + 1 vs *(p + 1)

The parentheses matter a lot. * runs before +, so these two expressions are completely different:

#include <stdio.h>

int main(void) {
    int levels[3] = {10, 20, 30};
    int *p = &levels[0];

    printf("*p + 1   = %d\n", *p + 1);    /* value 10, plus 1 */
    printf("*(p + 1) = %d\n", *(p + 1));  /* next element    */
    return 0;
}
*p + 1   = 11
*(p + 1) = 20

Subtracting two pointers

When two pointers point into the same array, subtracting them tells you how many elements apart they are (not bytes). The result has the type ptrdiff_t, which you print with %td.

#include <stdio.h>

int main(void) {
    int seats[8] = {0};
    int *first = &seats[0];
    int *mine = &seats[5];

    printf("Seats from the aisle: %td\n", mine - first);
    printf("Seats back the other way: %td\n", first - mine);
    return 0;
}
Seats from the aisle: 5
Seats back the other way: -5

With 4-byte ints the addresses are 20 bytes apart (5 × 4), yet the answer is 5, because C divides by the element size for you.

Comparing pointers

You can compare pointers into the same array with ==, !=, <, >, <= and >=. A pointer to a later element is "greater". This is perfect for a loop that runs until the pointer reaches the end. Let's find the first free parking spot:

#include <stdio.h>

int main(void) {
    int spots[6] = {1, 1, 0, 1, 0, 1};
    int *start = &spots[0];
    int *end = start + 6;   /* one past the last spot */
    int *p = start;

    while (p < end && *p != 0) {
        p++;
    }

    if (p < end) {
        printf("First free spot: %td\n", p - start + 1);
    } else {
        printf("The lot is full.\n");
    }
    return 0;
}
First free spot: 3

The pointer end points one past the last element. C allows you to create and compare that address, as long as you never read or write through it. That's why the loop checks p < end before *p.

Which pointer operations are allowed?

OperationExampleAllowed?Result
pointer + integerp + 2Yesa pointer 2 elements later
pointer - integerp - 1Yesa pointer 1 element earlier
pointer - pointerend - startYes (same array)number of elements between
compare pointersp < endYes (same array)true (1) or false (0)
pointer + pointerp + qNocompile error
multiply or dividep * 2Nocompile error

Adding two addresses is like adding two house addresses together: the result means nothing, so GCC stops you with invalid operands to binary +.

⚠️ Stay inside the array

Pointer arithmetic is only valid within one array, plus the single "one past the end" position. Moving a pointer before the first element or further past the end, or subtracting pointers into two different arrays, is undefined behavior, even if it seems to work.

Common mistakes with pointer arithmetic

1. Walking past the end of the array

A loop like for (int i = 0; i <= 5; i++) { sum += *p; p++; } on a 5-element array reads one element too many. It reads whatever happens to be next in memory, so you get a garbage value or a crash. Use <, and compare against an end pointer.

2. Thinking p + 1 means "one byte later"

It means one element later. Don't multiply by sizeof yourself: p + 1 * sizeof(int) would jump 4 elements, not 1.

3. Forgetting parentheses

*p + 1 adds 1 to the value; *(p + 1) reads the next element. When in doubt, add parentheses.

4. Mixing pointers from different arrays

If a and b are separate arrays, &b[0] - &a[0] has no meaning. The two arrays could be anywhere in memory.

Practice exercise

🏋️ Try it yourself

A weather app stores a week of temperatures: {18, 22, 19, 25, 21, 17, 20}. Without using any [i] index inside the loop, walk through the array with a pointer and find the warmest day. Print its temperature and day number (1 to 7) using pointer subtraction.

Show solution
#include <stdio.h>

int main(void) {
    int temps[7] = {18, 22, 19, 25, 21, 17, 20};
    int *start = &temps[0];
    int *end = start + 7;
    int *warmest = start;

    for (int *p = start + 1; p < end; p++) {
        if (*p > *warmest) {
            warmest = p;
        }
    }

    printf("Warmest: %d degrees on day %td\n", *warmest, warmest - start + 1);
    return 0;
}
Warmest: 25 degrees on day 4

What's next?

You've been writing &scores[0] to point at an array. It turns out C has a shortcut, because arrays and pointers are closely connected. Learn how in pointers and arrays in C.

Key takeaways

  • Pointer arithmetic moves a pointer between elements of the same array.
  • p + 1 moves forward one element, which is sizeof(type) bytes: 4 bytes for a typical int, 8 for a double.
  • p + n calculates a new address without changing p; p++ and p += n actually move p.
  • Subtracting two pointers into the same array gives the number of elements between them, printed with %td.
  • Pointers into the same array can be compared with <, >, == and !=, which is handy for loops that stop at an end pointer.
  • You cannot add, multiply or divide two pointers.
  • Stay inside the array; only the one-past-the-end address may be formed, and it must never be dereferenced.

Frequently asked questions

What is pointer arithmetic in C?

Pointer arithmetic is adding or subtracting integers to a pointer, subtracting two pointers, or comparing them. It is used to move a pointer through the elements of an array.

Why does incrementing a pointer add 4 instead of 1?

Incrementing a pointer moves it by one element, and the step is sizeof the type it points to. An int is usually 4 bytes, so an int pointer moves 4 bytes. A char pointer moves 1 byte and a double pointer usually moves 8.

What do you get when you subtract two pointers in C?

You get the number of elements between them, as a value of type ptrdiff_t, printed with %td. Both pointers must point into the same array (or one past its end).

Can you add two pointers in C?

No. Adding two addresses has no meaning, so the compiler reports an error. You can only add an integer to a pointer or subtract one pointer from another.

What is the difference between *p++ and (*p)++?

*p++ uses the value p points to and then moves the pointer to the next element. (*p)++ increases the value that p points to and leaves the pointer where it is.