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:
#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; }
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
#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; }
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:
#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; }
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:
#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; }
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).
#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; }
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.
Level yes rotor yes C yes pointer no