5.1.9 · D5C Programming
Question bank — Pointer arithmetic — adding integers to pointers
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
- Arrays decay to pointers
- Pointer subtraction and ptrdiff_t
- sizeof operator
- Strings and char pointers
- Multidimensional arrays and pointers
- Undefined behavior and bounds
- Dereference operator and precedence