What is Pointer Increment Precedence Explained – Embedded C Programming Course Online

Need Help or More Information?

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

Free C Programming Course

Pointer Increment Precedence Explained

Free embedded C course: how *p++, *++p, ++*p and (*p)++ really behave.

This lecture of our free C programming course answers the question every beginner asks: what is the difference between *p++ and ++*p? Pointer increment in C combines two operators with different jobs, and embedded C programming code is full of them in copy loops and frame parsers. By the end of this free C tutorial you will read all four forms at a glance and predict their output before you run them.

What You Will Learn

  • The precedence of postfix increment, prefix increment and the dereference operator
  • How *p++, *++p, ++*p and (*p)++ differ in pointer movement and data change
  • How to expand any pointer expression into plain statements
  • The copy and sum loops used in real firmware
  • Why changing a variable twice in one expression is undefined behaviour

Prerequisites

  • Pointer arithmetic from the previous lecture
  • Prefix and postfix increment on plain integers
  • A working C compiler on your computer

Why Pointer Increment in C Causes Confusion

Combine the dereference operator * with ++ and you get four expressions that look almost identical yet do four different jobs. Embedded programmers meet them constantly in copy loops, checksum loops and frame parsers, so it is worth learning to read each one at a glance.

The trick is to ask two questions for every expression. First, which operator binds to which operand? Second, is the increment prefix or postfix? The answers tell you whether the pointer moves, whether the data moves, and which value ends up in your variable.

Operator Precedence Rules You Need

The C standard groups operators by precedence. Three facts are enough for this lecture:

  • Postfix ++ and -- have higher precedence than unary *.
  • Prefix ++, prefix -- and unary * share one level, and that level groups from right to left.
  • Parentheses override everything.

Applying those rules gives the following reading of each expression. In all four cases p points at the first element of an int array, so the starting situation is the same.

ExpressionHow the compiler reads itWhat movesValue given to x
x = *p++*(p++)the pointer, after the readthe data at the old position
x = *++p*(++p)the pointer, before the readthe data at the new position
x = ++*p++(*p)the pointed-to data, before the readthe incremented data
x = (*p)++(*p)++the pointed-to data, after the readthe data before incrementing

Equivalent Statements for Each Form

The easiest way to remember the four forms is to expand each one into plain statements. Reading the expansion aloud removes the guesswork.

ExpressionSame as
x = *p++x = *p; then p = p + 1;
x = *++pp = p + 1; then x = *p;
x = ++*p*p = *p + 1; then x = *p;
x = (*p)++x = *p; then *p = *p + 1;
Array holds 40, 70, 90 and p starts at index 0: state after each expression
Expressionxp points to indexArray afterwards
*p++40140, 70, 90
*++p70140, 70, 90
++*p41041, 70, 90
(*p)++40041, 70, 90

Run All Four Forms Yourself

The program resets the array and the pointer before every form, so each line is an independent experiment. Save it as ptr_prec.c. It also includes the two loops you will actually use in firmware: a byte copy and a sum.

#include <stdio.h>

static void reload(int *d) { d[0] = 40; d[1] = 70; d[2] = 90; }

static void show(const char *label, int x, const int *d, const int *p)
{
    printf("%-6s x=%d  p at index %td  data={%d,%d,%d}\n",
           label, x, p - d, d[0], d[1], d[2]);
}

static void copy_bytes(char *dst, const char *src)
{
    while ((*dst++ = *src++) != '\0')
        ;
}

int main(void)
{
    int data[3];
    int *p;
    int x;

    reload(data); p = data; x = *p++;   show("*p++", x, data, p);
    reload(data); p = data; x = *++p;   show("*++p", x, data, p);
    reload(data); p = data; x = ++*p;   show("++*p", x, data, p);
    reload(data); p = data; x = (*p)++; show("(*p)++", x, data, p);

    char msg[16];
    copy_bytes(msg, "UART1");
    printf("copied: %s\n", msg);

    reload(data);
    int sum = 0;
    const int *q = data, *end = data + 3;
    while (q < end)
        sum += *q++;
    printf("sum = %d\n", sum);
    return 0;
}
gcc -std=c11 -Wall -Wextra -o ptr_prec ptr_prec.c
./ptr_prec

Expected output:

*p++   x=40  p at index 1  data={40,70,90}
*++p   x=70  p at index 1  data={40,70,90}
++*p   x=41  p at index 0  data={41,70,90}
(*p)++ x=40  p at index 0  data={41,70,90}
copied: UART1
sum = 200

What the output tells you

  • For *p++ the variable x is 40 and p has moved to index 1, but the array is untouched.
  • For *++p the pointer moved first, so x is 70 and p is again at index 1.
  • For ++*p the pointer never moves. The first array element grew from 40 to 41 and x received the new value.
  • For (*p)++ the value is copied first, so x is still 40, and only then is the array element incremented to 41. The pointer stays at index 0.

Real Embedded Idioms Built on Pointer Increment in C

The classic byte copy

The loop while ((*dst++ = *src++) != '\0') copies a string including its terminator. Read it right to left: fetch the byte at src, advance src, store the byte at dst, advance dst, then test the stored value. It is compact, but the exact same work written with an index is equally fast on a modern compiler, so use whichever your team finds more readable.

The pointer-walk sum

In the sample, sum += *q++ while q < end reads one element, adds it and advances in one step. The end pointer is computed once as data + 3, so the loop test is a single pointer comparison.

Never touch the same object twice in one expression

Writing something like *p++ = *p modifies p and reads it in the same expression without a sequence point between them. That is undefined behaviour, and different compilers or optimisation levels can give different answers. Split such code into two statements.

Everything in this lecture applies to decrement too. *p-- reads then steps back, *--p steps back then reads, --*p decrements the data, and (*p)-- decrements the data after reading it.

Performance and Readability of Pointer Increment in C

Compilers on ARM, RISC-V and x86 recognise these patterns and often emit a single post-indexed load instruction for *p++. That is a real advantage on small cores, but the readable alternative is almost always just as good after optimisation. The cost that does not go away is human: an expression with three operators in it is easy to misread during code review.

A sensible team rule is to allow *p++ in tiny well-known loops such as copies and walks, and to write everything else as two statements.

Common Mistakes with Pointer Increment in C

  • Writing *p++ when the intent was to increment the data; that needs (*p)++.
  • Forgetting the parentheses in (*p)++, which silently moves the pointer instead.
  • Combining a pointer update with a read of the same pointer in one expression.
  • Assuming prefix and postfix forms are interchangeable inside a larger expression.
  • Copying strings without a terminator check.

Best Practices for Embedded C Programming

  • Use pointer increment in C with *p++ only in tiny, idiomatic loops.
  • Expand anything complicated into two statements.
  • Add parentheses even when they are redundant if they make the intent obvious.
  • Compile with -Wall -Wextra so the compiler warns about suspicious sequences.
  • Write a short host-side test for every parsing loop before moving it to the target.

Key Takeaways

  • Pointer increment in C follows precedence: postfix ++ binds tighter than *, so *p++ means *(p++).
  • *++p and ++*p read right to left: prefix increment applies to whatever is closest on its right.
  • (*p)++ needs its parentheses to increment the data instead of the pointer.
  • The same rules hold for the decrement operator.
  • Never read and modify the same object in one expression.

Conclusion

Pointer increment in C comes down to four expressions and two questions: which operand does the increment attach to, and is it prefix or postfix? Answer those and the behaviour is fixed. The program in this lecture gives you a ready-made experiment you can modify, for example by switching to decrement or to a char array.

The next lecture in this free embedded C course looks at comparing pointers, which you need to decide when a loop has reached the end of a buffer.

Frequently Asked Questions

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

In *p++ the pointer is incremented after the value it pointed to is read. In (*p)++ the pointer stays where it is and the value it points to is incremented after being read.

What does *++p do?

It increments the pointer first, then reads the data at the new address.

What does ++*p do?

It increments the value that p points to and gives you the new value. The pointer itself does not move.

Which has higher precedence, the dereference operator or postfix ++?

Postfix increment has higher precedence, so *p++ is parsed as *(p++).

Do the same rules apply to decrement?

Yes. Prefix and postfix decrement follow the same precedence and associativity rules as increment.

Is *p++ = *p safe?

No. It reads and modifies p in the same expression with no sequence point, which is undefined behaviour. Split it into two statements.

Is this a good topic for embedded C interviews?

Yes. Questions on *p++ versus ++*p appear often because they test precedence, pointer movement and careful reading together.

Master Embedded C Programming for Free

Keep going through our free C programming course and learn how pointers are compared next.

Need Help or More Information?

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

2 Comments

Leave a Reply

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