What are Pointer Increment Operator Precedence- Learn C Programming Online Free

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

Pointer Increment Operator Precedence

Decode *p++, *++p, ++*p and (*p)++ with traces and verified output. Free C programming course for embedded systems.

free c programming coursefree c tutorialc programming for embedded systemsembedded c programmingpointer increment operator

What You Will Learn in This Lecture

  • How operator precedence decides what *p++, *++p, ++*p and (*p)++ actually do
  • The difference between moving a pointer and changing the value it points to
  • How to trace a pointer expression with a three-column table: pointer position, pointed value and result
  • Why ++*p in a loop never advances the pointer
  • How these forms appear in real embedded C programming, such as buffer copy loops and register access

Prerequisites

You should know pointer arithmetic and the dereference operator from the earlier lectures of this free C programming course. A quick reminder of C operator precedence will help but is not required: the key facts are explained below.

The One Rule Behind Every Pointer Increment Operator Form

Two operators fight for the same pointer in these expressions: the dereference * and the increment ++. The outcome depends on two facts.

  1. **Postfix ++ has higher precedence than prefix *.** So *p++ groups as *(p++).
  2. **Prefix ++ and prefix * have equal precedence and group right to left.** So *++p is *(++p) and ++*p is ++(*p).

Postfix increment yields the **old** value of its operand and then increments; prefix increment increments and yields the **new** value. Apply that to either the pointer or the pointee and all four forms follow.

ExpressionGroupingWhat movesValue produced
*p++*(p++)the pointer, after the readthe element the pointer **was** on
*++p*(++p)the pointer, before the readthe next element
++*p++(*p)the pointed-to value, up by 1the **new** value
(*p)++parenthesesthe pointed-to value, up by 1the **old** value

Who changes: pointer or data?

*p++ p moves → data unchanged
*++p p moves → data unchanged
++*p p stays → data changes
(*p)++ p stays → data changes

Tip: Put the parentheses in your own code. Even if you know the precedence table by heart, (*p)++ communicates “increment the value” to the next reader, and it costs nothing at run time.

Tracing Pointer Expressions with a Complete Program

The helper show() prints the pointer position as an index, the value it points at and the variable x that receives each expression. Each line applies one form to the same pointer, so the effects accumulate, exactly as they would in a real function.

#include <stdio.h>

static void show(const char *tag, const int *base, const int *p, int x)
{
    printf("%-10s idx=%ld  *p=%d  x=%d\n", tag, (long)(p - base), *p, x);
}

int main(void)
{
    int data[4] = { 10, 20, 30, 40 };
    int *p = data;
    int x = 0;

    show("start", data, p, x);
    x = *p++;     show("x = *p++",  data, p, x);
    x = *++p;     show("x = *++p",  data, p, x);
    x = ++*p;     show("x = ++*p",  data, p, x);
    x = (*p)++;   show("x=(*p)++",  data, p, x);
    printf("data = %d %d %d %d\n", data[0], data[1], data[2], data[3]);
    return 0;
}
gcc -std=c17 -Wall -Wextra -o ptr_inc ptr_inc.c
./ptr_inc

Expected output:

start      idx=0  *p=10  x=0
x = *p++   idx=1  *p=20  x=10
x = *++p   idx=2  *p=30  x=30
x = ++*p   idx=2  *p=31  x=31
x=(*p)++   idx=2  *p=32  x=31
data = 10 20 32 40

Trace it line by line:

  • x = *p++: x receives 10 (the old position), then p advances to index 1.
  • x = *++p: p advances to index 2 first, so x becomes 30.
  • x = ++*p: the pointer stays at index 2; data[2] becomes 31 and x gets 31.
  • x = (*p)++: x gets the old 31, then data[2] becomes 32. The pointer still has not moved.

Pointer position after each statement

start 10203040
*p++ 10203040
*++p 10203040
++*p 10203140
(*p)++ 10203240

Walking Forward and Backward with *p++ and *–p

The most useful form in practice is *p++ as the body of a copy or print loop. Its mirror image for walking backward is *--p: decrement first, then read, which is the right order when the pointer starts one past the last element.

#include <stdio.h>

int main(void)
{
    int v[4] = { 5, 6, 7, 8 };
    int *p = v;
    for (int i = 0; i < 4; i++)
        printf("%d ", *p++);
    printf("\n");
    for (int i = 0; i < 4; i++)
        printf("%d ", *--p);
    printf("\n");

    int w[3] = { 1, 1, 1 };
    int *q = w;
    for (int i = 0; i < 3; i++) {
        (*q)++;
        printf("%d ", *q);
        q++;
    }
    printf("\n");
    return 0;
}

Expected output:

5 6 7 8
8 7 6 5
2 2 2

After the first loop p is one past the end of v; forming that address is legal. *--p then steps back onto v[3] before reading. The third loop uses (*q)++ to bump each element from 1 to 2 and then q++ to move on, so the pointer and the value changes are two separate operations.

Why ++*p in a Loop Never Moves the Pointer

A classic trap: a loop that prints ++*p repeatedly looks like it should walk the array, but the pointer is never incremented. Only the first element changes.

#include <stdio.h>

int main(void)
{
    int v[3] = { 1, 2, 3 };
    int *p = v;
    for (int i = 0; i < 4; i++)
        printf("%d ", ++*p);
    printf("\n");
    for (int i = 0; i < 3; i++)
        printf("%d ", (*p)++);
    printf("\n");
    printf("v = %d %d %d, p still at index %ld\n", v[0], v[1], v[2], (long)(p - v));
    return 0;
}

Expected output:

2 3 4 5
5 6 7
v = 8 2 3, p still at index 0

Every iteration hits v[0]. The first loop raises it from 1 to 5. The second loop uses the postfix form, so it prints the old values 5, 6, 7 and leaves 8 behind. Neither v[1] nor v[2] was touched, and p - v is still 0. Notice also that the loop ran four times on a three-element array; that was safe only because the loop never moved p. Combine ++*p with p++ carelessly and you walk off the end.

Real-World Uses in Embedded C Programming

  • **Byte copy loop:** *dst++ = *src++; copies one byte and advances both pointers in a single statement. It is the heart of many memcpy and strcpy implementations.
  • **Reading a FIFO register:** *buf++ = *UART_DR; stores the register value and advances the buffer pointer. Here UART_DR would be a volatile pointer so every read really happens.
  • **Counting events:** (*counter)++; increments a counter owned by the caller, for example a statistics field passed by pointer into an interrupt handler.
  • **Parsers:** *++cursor skips ahead before reading the next token character.

Common Mistakes and Troubleshooting

MistakeWhat happensFix
Writing *p++ when (*p)++ was meantThe pointer moves and the data is unchangedAdd parentheses and read the line aloud
Writing ++*p in a loop expecting traversalThe same element is incremented repeatedlyUse p++ or *p++ to advance
Reading *--p when p already sits on the first elementReads before the array startStart from one past the end, or test p > start first
Modifying the same variable twice in one expression, such as *p++ = *pUnspecified order of evaluation, undefined behaviour in some formsSplit into separate statements
Using *p++ on a const table and assigning through itCompile error on writeUse const int * for read-only data

Best Practices, Performance and Security

  • One side effect per statement. Compilers generate the same code for the clear version, and reviewers will thank you.
  • *dst++ = *src++ maps to post-increment addressing modes on ARM Cortex-M and many other targets, so it is both compact and fast.
  • When copying data from outside, pass the length and compare the pointer against an end pointer. Unbounded *dst++ = *src++ loops are a standard buffer overflow pattern.
  • Use volatile for pointers into hardware registers so the compiler does not remove or reorder the reads.
  • Run your code under -fsanitize=undefined,address during testing to catch an off-by-one walk before it reaches the board.

Summary and Key Takeaways

  • Postfix ++ binds tighter than *, so *p++ means *(p++): read the old position, then move.
  • *++p moves first and then reads.
  • ++*p and (*p)++ change the pointed-to value, not the pointer.
  • Postfix forms return the old value; prefix forms return the new value.
  • When in doubt, use parentheses and one operation per statement.

Conclusion

These four forms cover most of the output-prediction questions you will meet in a free C tutorial or a firmware interview. Build the three-column trace habit: pointer index, pointed value, result variable. After a few traces the answers become mechanical. The next lecture builds on this by using pointer comparison to control loops that go forward, in steps, and in reverse.

Frequently Asked Questions

What does *p++ do in C?

It reads the value at the current pointer position and then advances the pointer by one element. It is parsed as *(p++).

What is the difference between *p++ and (*p)++?

*p++ moves the pointer and leaves the data alone. (*p)++ leaves the pointer where it is and increments the value it points to.

What is the difference between *++p and ++*p?

*++p advances the pointer and then reads the new element. ++*p increments the value at the current position and yields the new value.

Does postfix or prefix increment have higher precedence than the dereference operator?

Postfix ++ has higher precedence than unary *. Prefix ++ and unary * have the same precedence and group right to left.

Is *dst++ = *src++ safe?

It is well defined, but it has no bounds check. Always limit the loop with a length or an end pointer when the source is untrusted.

Why is the order of evaluation a problem with expressions like *p++ = *p?

The language does not fix when the side effect on p happens relative to other reads of p in the same expression, so the result can be undefined. Use separate statements.

Keep Going in the Free Embedded C Course

Next lecture: pointer loops that run forward, in steps and in reverse.

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *