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.
#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
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:
#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:
#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
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 startsp + nadvancespbyn * sizeof(*p)bytes, landing on then-th element after it.p2 - p1(both pointing into the same array) gives the element count between them, typedptrdiff_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.