Skip to content

Intermediate · Pointers · topic 24 of 60 · 25 min

Pointer arithmetic

Explanation

Adding 1 to a pointer moves it to the NEXT element, not the next byte: for an int pointer, p + 1 is 4 bytes further on typical systems. Subtracting two pointers into the same array gives the number of elements between them (type ptrdiff_t). Pointers can point one past the end of an array but must not be dereferenced there, and arithmetic outside the array is undefined.

Moving by elements, not bytes

output
int a[4] = {10, 20, 30, 40};
int *p = a;            /* p = 0x1000 */

p        → 0x1000   *p       → 10
p + 1    → 0x1004   *(p + 1) → 20   (moved sizeof(int) = 4 bytes)
p + 3    → 0x100C   *(p + 3) → 40

char *c = (char *) a;  /* the same memory seen as bytes */
c + 1    → 0x1001   (moved 1 byte)

double d[3];  double *q = d;   /* q = 0x2000 */
q + 1    → 0x2008   (moved sizeof(double) = 8 bytes)
The compiler multiplies by the size of the pointed-to type for you.

Increment, subtraction and comparison

c
int marks[5] = {78, 85, 91, 66, 72};
int *first = &marks[0];
int *last = &marks[4];
printf("%td\n", last - first);      /* 4: elements between them (%td prints ptrdiff_t) */

for (int *p = marks; p < marks + 5; p++) {   /* comparing pointers into the same array */
    printf("%d ", *p);
}

int *p = marks;
printf("%d\n", *p++);   /* 78: uses *p, then moves p (postfix ++ binds tighter than *) */
printf("%d\n", *p);     /* 85 */

The one-past-the-end rule

C lets a pointer point exactly one element past the end of an array (marks + 5 above), so loops like p < marks + 5 are legal. Dereferencing that pointer is undefined behaviour, and so is computing a pointer further outside the array (marks + 6, or marks - 1), even without dereferencing it.

Precedence traps

ExpressionMeans
*p++*(p++): read the current element, then move p
(*p)++increment the VALUE p points to
*++pmove p first, then read
++*pincrement the value, then read it

What you cannot do

  • Add two pointers (p + q has no meaning; subtracting them does).
  • Subtract or compare pointers into DIFFERENT arrays (undefined).
  • Do arithmetic on void * in standard C (GCC allows it as an extension, treating the size as 1; do not rely on it). Cast to char * or unsigned char * for byte-wise work.
  • Multiply or divide pointers.
Careful: Pointer arithmetic does not check bounds. A loop condition off by one (p <= a + n) dereferences one past the end; a wrong cast (treating a char buffer as int *) can read misaligned data, which crashes on some processors and violates the strict-aliasing rules on all. Keep arithmetic within one array and use the right pointer type.
Think of it like this: Pointer arithmetic is like moving along a train by coaches, not by metres. "Two coaches ahead" means two whole coaches, however long each coach is: for an int train each coach is 4 metres, for a double train 8 metres. The guard (the compiler) does the metre calculation for you.
Tip: When debugging pointer code, print both the address (%p) and the index (p - start) of each pointer. The index is usually what you actually care about.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Pointer arithmetic · 1/6

Pointer arithmetic

In this lesson you will move pointers with arithmetic.

Prefer a video by a teacher? Find free videos on “c pointer arithmetic explained increment subtraction one past the end” on YouTube ↗

In real life: Scanning a network packet

A network tool reads a packet as a buffer of bytes and uses an unsigned char pointer to step through its fields: a 1-byte type, a 2-byte length, then the payload. Pointer arithmetic on unsigned char moves exactly one byte at a time, and the code checks the remaining length before every read.

c
int parse_packet(const unsigned char *buf, size_t len) {
    const unsigned char *p = buf, *end = buf + len;
    if (end - p < 3) return -1;                       /* need type + 2 length bytes */
    unsigned type = p[0];
    unsigned payload_len = (unsigned)(p[1] << 8 | p[2]);   /* big-endian 16-bit length */
    p += 3;
    if ((size_t)(end - p) < payload_len) return -1;   /* never read past the buffer */
    printf("type %u, %u payload bytes\n", type, payload_len);
    return 0;
}

Practice (0/3 done)

Task 1 · Question

int a[5]; int *p = &a[1], *q = &a[4]; What is q - p?

Task 2 · Question

What does this print? int a[] = {1, 2, 3}; int *p = a; int x = *p++; int y = (*p)++; printf("%d %d %d\n", x, y, a[1]);

Task 3 · Practice

Without using any [ ] subscripts, write void reverse(int *start, int *end) that reverses an array in place using two pointers that move toward each other (end points one past the last element). Draw the memory diagram for reversing {1, 2, 3, 4}.

Quick quiz

Question 1

1.If double *q holds 0x2000, what is q + 2?

Question 2

2.Which printf format prints a ptrdiff_t?

Question 3

3.Is it legal to compute a + n for int a[n]?

Question 4

4.What does (*p)++ do?

Read more (free)

Before this, learn

This topic builds on these. If something here feels new, open them first.

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

If double *q holds 0x2000, what is q + 2?

Answer: 0x2010

2 × 8 bytes = 16 = 0x10.

Which printf format prints a ptrdiff_t?

Answer: %td

t is the length modifier for ptrdiff_t.

Is it legal to compute a + n for int a[n]?

Answer: Yes, one past the end may be computed but not dereferenced

The one-past-the-end rule.

What does (*p)++ do?

Answer: Increments the value p points to

The brackets apply ++ to the value.

How should a pointer be printed with printf?Related

Answer: %p with a (void *) cast

%p expects a void pointer.

From Addresses and pointers

What is 17 / 5 in Java with ints?Related

Answer: 3

Integer division drops the decimal part.

From Operators and expressions

Where does an array NOT decay to a pointer?Related

Answer: In sizeof a

sizeof (and &a) see the whole array.

From Pointers and arrays

Which expression is undefined behaviour?Related

Answer: i = i++ + 1

i is modified twice without sequencing.

From Operators

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 1 more topic in “Pointers” before its exam: NULL, dangling pointers and pointers to pointers.
  2. Module 5 exam: Pointers · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Dynamic memory. 60 topics left in the course before the final exam.

← Previous: Pointers and arrays