Pointer Arithmetic

You can add integers to pointers, subtract them, and compare pointers into the same array. The arithmetic counts in elements, not bytes, which makes it a natural way to walk through arrays and strings — and the reason so much classic C code is written with pointers instead of indexes.

Moving by elements

Adding 1 to an int * moves it to the next int — 4 bytes further on most systems. Adding 1 to a double * moves 8 bytes:

C
#include <stdio.h>

int main(void) {
    int numbers[] = { 10, 20, 30, 40 };
    double values[] = { 1.5, 2.5, 3.5 };

    int *ip = numbers;
    double *dp = values;
    printf("%d %d\n", *ip, *(ip + 2));
    printf("%.1f %.1f\n", *dp, *(dp + 1));

    printf("int step: %td bytes, double step: %td bytes\n",
           (char *) (ip + 1) - (char *) ip, (char *) (dp + 1) - (char *) dp);
    return 0;
}
Output
10 30
1.5 2.5
int step: 4 bytes, double step: 8 bytes

Casting to char * (one byte per step) reveals the real distances. %td prints a ptrdiff_t, the type of a pointer difference.

Walking with a pointer

C
#include <stdio.h>

int main(void) {
    int data[] = { 3, 1, 4, 1, 5, 9, 2, 6 };
    size_t n = sizeof data / sizeof data[0];

    int sum = 0;
    for (const int *p = data; p < data + n; p++) {
        sum += *p;
    }
    printf("sum = %d\n", sum);
    return 0;
}
Output
sum = 31

data + n points one element past the end. You may form and compare that address — it's the standard "end" marker — but never dereference it.

Strings with pointers

Many classic string functions are short pointer loops. Here's how strlen and a copy could be written:

C
#include <stdio.h>

size_t my_strlen(const char *s) {
    const char *p = s;
    while (*p != '\0') p++;
    return (size_t) (p - s);
}

void my_copy(char *dst, const char *src) {
    while ((*dst++ = *src++) != '\0') {
        // copy each character, including the final '\0'
    }
}

int main(void) {
    char buffer[16];
    my_copy(buffer, "pointer");
    printf("%s has %zu characters\n", buffer, my_strlen(buffer));
    return 0;
}
Output
pointer has 7 characters

*dst++ = *src++ copies one character and advances both pointers. It's a famous idiom — compact, but write the longer form if it reads better to you.

Subtracting pointers

Subtracting two pointers into the same array gives the number of elements between them:

C
#include <stdio.h>
#include <string.h>

int main(void) {
    const char *path = "/var/log/app/server.log";
    const char *slash = strrchr(path, '/');
    printf("file name starts at index %td: %s\n", slash + 1 - path, slash + 1);
    printf("directory: %.*s\n", (int) (slash - path), path);
    return 0;
}
Output
file name starts at index 13: server.log
directory: /var/log/app

Comparing pointers

== and != work on any two pointers of the same type. <, > and subtraction are only meaningful for pointers into the same array (or one past its end).

C
#include <stdio.h>

void reverse(int *start, int *end) {   // end points one past the last element
    while (start < --end) {
        int tmp = *start;
        *start++ = *end;
        *end = tmp;
    }
}

int main(void) {
    int v[] = { 1, 2, 3, 4, 5 };
    reverse(v, v + 5);
    for (int i = 0; i < 5; i++) printf("%d ", v[i]);
    printf("\n");
    return 0;
}
Output
5 4 3 2 1

The rules

  • Pointer arithmetic is only defined within one array object, plus the position one past its end.
  • Going outside that range — even without dereferencing — is undefined behaviour.
  • You can't add two pointers, or do arithmetic on a void * in standard C.
  • When an index is clearer, use an index. Compilers generate equally fast code for both styles.

Try it yourself

Check whether a string is a palindrome using two pointers that move towards each other, ignoring case.

Try it yourself · main.c
Edit the code, copy it into main.c, then compile and run it with cc -std=c17 -Wall main.c -o main && ./main. How to set up a C compiler
Output
Level    yes
rotor    yes
C        yes
pointer  no