p + 1 does not mean "add one byte". Pointer arithmetic advances in elements, pointer subtraction returns an element count, and a loop may reach one position beyond an array but must never read there. Correct tracing tracks three things after every operation: the pointer's array index, whether that position is dereferenceable, and the resulting value or output. These rules also underpin array traversal in the Coding & DSA Courses for Placements learning path.
1. The mental model: pointer arithmetic moves in elements
Start with:
int values[5] = {11, 22, 33, 44, 55};
int *p = &values[1];Here, p points to element 1, so *p is 22. The expression p + 2 points to element 3, so *(p + 2) is 44. It advances across two complete int objects, whatever sizeof(int) is on that implementation.
The useful operations are p + n, p - n, p++, p--, p += n, and p -= n. Subtracting two pointers into the same array gives the number of elements between them. C does not define adding two pointers, multiplying a pointer, or dividing a pointer.
For a valid index i, values[i] is equivalent to *(values + i). That is an expression equivalence. It does not make an array object and a pointer object identical types.
2. Complete worked example: move, dereference, and subtract
This C17 program keeps each operation visible:
#include <stddef.h>
#include <stdio.h>
int main(void) {
int values[5] = {11, 22, 33, 44, 55};
int *p = &values[1];
int *q = &values[4];
printf("*p = %d\n", *p);
printf("*(p + 2) = %d\n", *(p + 2));
printf("*(q - 1) = %d\n", *(q - 1));
printf("q - p = %td\n", q - p);
++p;
printf("after ++p, *p = %d\n", *p);
return 0;
}Trace it before looking at the output:
pbegins at index 1, whose value is 22.p + 2moves from index 1 to index 3, whose value is 44.qis at index 4, soq - 1is index 3, again giving 44.q - pis4 - 1 = 3elements. It is not a byte count.++pmovespfrom index 1 to index 2, whose value is 33.
Pointer subtraction has type ptrdiff_t, declared through <stddef.h>. The %td conversion prints that type. The exact output is:
*p = 22
*(p + 2) = 44
*(q - 1) = 44
q - p = 3
after ++p, *p = 33Save the source as pointer_arithmetic.c, then compile it with:
cc -std=c17 -Wall -Wextra -pedantic pointer_arithmetic.c -o pointer_arithmetic
3. Traverse an array safely with a one-past end pointer
A pointer loop can use the position after the last element as its stopping sentinel:
int numbers[4] = {3, 6, 9, 12};
int total = 0;
for (int *it = numbers; it != numbers + 4; ++it) {
total += *it;
}
printf("total = %d\n", total);The running total is 0 -> 3 -> 9 -> 18 -> 30, so the program prints total = 30.
numbers + 4 is a permitted one-past pointer. The iterator may be compared with it, but *(numbers + 4) is invalid because that pointer does not designate an element. The loop tests it != numbers + 4 before dereferencing, so the body never reads the sentinel. C Programming & Data Structures gives the broader roadmap for using this array-pointer relationship in data structures.

4. Pointer subtraction and comparisons have an array boundary
With the earlier values array, these calculations are valid:
&values[4] - &values[1]is4 - 1 = 3.&values[1] - &values[4]is1 - 4 = -3.&values[2] < &values[4]is true.
The difference is measured in elements, not bytes. As a safe beginner rule, subtract or order pointers only when they refer to elements of the same array object, including its one-past position. Do not subtract pointers into two independent arrays, even if printed machine addresses appear close.
Comparison and dereference are separate questions. values + 5 may serve as the end pointer for this five-element array, and values + 5 - values is 5. However, values[5] and *(values + 5) are both out of bounds.
5. The pointed-to type controls the stride
Consider three arrays without assuming any concrete platform sizes:
char letters[3] = {'A', 'B', 'C'};
int counts[3] = {10, 20, 30};
double rates[3] = {1.5, 2.5, 3.5};letters + 1 reaches 'B', counts + 1 reaches 20, and rates + 1 reaches 2.5. In each case, the pointer moves by one complete element of its own pointed-to type.
The type model uses the size represented by sizeof *ptr, but programmers write ptr + 1, not ptr + sizeof *ptr. C applies the scaling automatically. An unsigned char * may inspect an object's representation byte by byte, but that is a separate operation from traversing an int array. Do not cast an int * merely to force byte-sized movement.
6. Common errors: bounds, unrelated pointers, and *p++
Four forms deserve an immediate check:
p + qhas no defined pointer-addition meaning.Arithmetic on a null pointer is invalid.
a_end - b_startis not a valid distance whenaandbare different arrays.Forming or dereferencing a pointer outside an array plus its one-past position is invalid.
The repair is to preserve the array base and a verified element count, then keep every computed pointer within that range.
Precedence creates a different trap:
int a[2] = {7, 9};
int *p = a;
int x = *p++;*p++ parses as *(p++). Therefore, x becomes 7 and p moves to &a[1]. Reset p = a, then evaluate int y = (*p)++;. Now y becomes 7, a[0] becomes 8, and p remains at &a[0].
By contrast, *++p moves the pointer first and reads the new element, while ++*p increments the pointed-to integer. Use parentheses when the intent is not immediately obvious, and compile with warnings. A precedence mistake can still be valid C.
7. How exams and interviews test pointer arithmetic
Stable question forms ask you to predict output, identify the element reached, calculate a same-array difference, spot a one-past dereference, or distinguish *p++ from (*p)++.
Dry-run this example:
int a[4] = {2, 4, 8, 16};
int *p = a + 1;
printf("%d %td\n", *(p + 2), (a + 4) - p);It prints 16 3. The pointer p begins at index 1, so p + 2 reaches index 3 and reads 16. The one-past pointer a + 4 is three elements after index 1, so (a + 4) - p is 4 - 1 = 3.
Draw index positions before thinking about possible byte addresses. C Programming for Teaching CS Exams extends the exam-oriented view, while Coding for Placements supports broader coding-test practice.
8. Practice checks, the short version, and next step
Answer these before running them:
For
int v[] = {5, 10, 15, 20},*(v + 2)is 15.If
int *p = &v[3], thenp - vis 3.v + 4may be an end pointer, but it must not be dereferenced.With
p = v,*p++yields 5, then movespto&v[1].
Keep four rules: pointer movement is in elements; arithmetic stays within one array plus its one-past position; subtraction yields an element count; and one-past is a sentinel, not an element. If you want to study pointers inside a complete C sequence, continue with the C Language Course: Concepts, MCQs & Coding.




