5.1.9 · D5C Programming

Question bank — Pointer arithmetic — adding integers to pointers

1,418 words6 min readBack to topic

Read the whole line, commit to an answer, then reveal. If your reasoning differs from the answer even when the verdict matches, you have a hidden gap.


True or false — justify

p + 1 moves the pointer forward by exactly one byte.
False — it moves by ==sizeof(*p)== bytes, so an int* jumps 4 (or whatever the int size is) and lands on the next element, not the next byte.
arr[i] and *(arr + i) compile to the same thing.
True — indexing is defined as this pointer expression; the compiler literally rewrites arr[i] as *(arr + i). See Arrays decay to pointers.
i[arr] is a syntax error.
False — since arr[i] means *(arr + i) and + is commutative, i[arr] means *(i + arr), the same element. Ugly, but legal.
For char *c, c + 1 and ((int*)c) + 1 advance the same distance.
False — c + 1 moves 1 byte (sizeof(char)), while the int* version moves sizeof(int) bytes. The stride follows the pointer's type, not the address value.
Adding two pointers p + q is legal because subtracting them is.
False — only pointer ± int and pointer − pointer are defined. "Address plus address" has no meaning, so p + q won't even compile.
p - q gives the number of bytes between two pointers.
False — it gives the number of elements (a ptrdiff_t), i.e. the byte gap divided by sizeof(*p). See Pointer subtraction and ptrdiff_t.
Forming the address arr + N (one past the last element of arr[N]) is undefined behavior.
False — forming the one-past-the-end pointer is explicitly legal; only dereferencing it is undefined behavior. See Undefined behavior and bounds.
*p + 1 and *(p + 1) are just spacing variants of the same expression.
False — * binds tighter than +, so *p + 1 is "value at p, then add 1", while *(p + 1) is "value of the next element". Different operations, usually different results. See Dereference operator and precedence.
p++ and p + 1 change p in the same way.
False — p + 1 computes a new address but leaves p unchanged; p++ reassigns p to that new address (and evaluates to the old one). Only p++ mutates the pointer.
On a machine where sizeof(int) == 8, (int*)0x100 + 1 equals 0x104.
False — the stride is sizeof(int), so it equals 0x100 + 8 = 0x108. Never assume 4; the offset scales with whatever sizeof(int) actually is on that machine.

Spot the error

int a[3]; int *p = a; printf("%d", *(a + 3)); — what's wrong?
a + 3 is the one-past-end pointer; forming it is fine but dereferencing it (the *) is undefined behavior — you read outside the array.
char *s = "hi"; int *p = s; p = p + 1; — why is this dangerous?
Reinterpreting a char* as int* then stepping advances by sizeof(int) bytes over data that was laid out as single chars, so p no longer aligns to real elements. See Strings and char pointers.
int a[4]; int *p = a; if (*p + 1 == a[1]) ... — intent was "next element equals". Fix it.
*p + 1 is a[0] + 1, not the next element. The author meant *(p + 1), which is a[1]. Parentheses were dropped.
int *p = a; ((char*)p) + 2; used to skip 2 ints — does it?
No — casting to char* sets the stride to 1 byte, so +2 moves only 2 bytes, landing in the middle of a[0]. To skip 2 ints, add 2 to the int* directly.
int a[5]; for (int *p = a; p <= a + 5; p++) sum += *p; — bug?
The <= lets the loop run when p == a + 5 (one past end) and dereference it — undefined behavior. Use p < a + 5.
double *d = (double*)0x1000; d + 1; — a student says this is 0x1001.
Wrong — the stride is sizeof(double) (often 8), so it's 0x1008. The number +1 is elements, scaled by the type. See sizeof operator.
int m[2][3]; int *p = m; p = p + 1; — does p now point at the next row?
No — m decays to int (*)[3], but assigning to a plain int* (with a cast/warning) makes +1 advance one int, i.e. one column, not one row. See Multidimensional arrays and pointers.

Why questions

Why did C's designers scale p + n by sizeof(*p) instead of using raw bytes?
Because arrays are stored contiguously, so element i sits at byte offset i × sizeof(T); scaling makes *(p + i) land exactly on arr[i] with no manual multiplication.
Why is pointer − pointer defined but pointer + pointer is not?
A difference of two addresses is a meaningful distance (how many elements apart); a sum of two addresses is a location with no interpretation, so the language forbids it.
Why does q - p divide by sizeof(*p) rather than returning the raw byte gap?
So the result answers "how many elements between them", matching how p + n was defined — the two operations are inverses in element units, not bytes.
Why is the one-past-the-end pointer allowed to exist at all?
So loops can use p < end as a clean stop condition without ever computing an address the standard calls illegal — you may hold arr + N, just not read through it.
Why does casting to char* before arithmetic give you true byte-level stepping?
Because sizeof(char) is guaranteed to be 1, so on a char* every +1 is exactly one byte — the stride collapses to the smallest unit.
Why must the pointer "know" its type even though an address is just a number?
The raw address alone can't tell the compiler how far to jump; the type supplies sizeof(*p), which is the stride length baked into every +, -, ++, and [].

Edge cases

Is p + 0 legal, and what does it give?
Legal — it's just p itself (stride times zero is zero bytes). Even a null pointer plus 0 is defined; adding any nonzero integer to null is undefined.
Can you validly form a pointer before the first element, like a - 1?
No — unlike the one-past-end case, forming a pointer below &a[0] is undefined behavior, even if you never dereference it.
If sizeof(int) == 1 on some exotic system, does p + 1 for an int* move 1 byte?
Yes — the rule is n × sizeof(T), so if sizeof(int) truly were 1, an int* would step 1 byte. The rule holds; only the number changes.
Two pointers into different arrays — is subtracting them meaningful?
No — q - p is only defined when both point into (or one-past) the same array; across separate arrays the result is undefined behavior.
Does incrementing a pointer past one-past-the-end (p = end + 1) merely give a "big number"?
No — even computing an address more than one past the end is undefined behavior; the compiler may assume it never happens and optimize accordingly. See Undefined behavior and bounds.
For a zero-length situation like an empty array region, is p == p + 0 guaranteed?
Yes — p + 0 always equals p, so comparing a pointer to itself-plus-zero is always true, even with no elements to walk.

Connections