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.
What You Will Learn in This Lecture
- How operator precedence decides what
*p++,*++p,++*pand(*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
++*pin 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.
- **Postfix
++has higher precedence than prefix*.** So*p++groups as*(p++). - **Prefix
++and prefix*have equal precedence and group right to left.** So*++pis*(++p)and++*pis++(*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.
| Expression | Grouping | What moves | Value produced |
|---|---|---|---|
*p++ | *(p++) | the pointer, after the read | the element the pointer **was** on |
*++p | *(++p) | the pointer, before the read | the next element |
++*p | ++(*p) | the pointed-to value, up by 1 | the **new** value |
(*p)++ | parentheses | the pointed-to value, up by 1 | the **old** value |
Who changes: pointer or data?
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_incExpected 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 40Trace it line by line:
x = *p++:xreceives 10 (the old position), thenpadvances to index 1.x = *++p:padvances to index 2 first, soxbecomes 30.x = ++*p: the pointer stays at index 2;data[2]becomes 31 andxgets 31.x = (*p)++:xgets the old 31, thendata[2]becomes 32. The pointer still has not moved.
Pointer position after each statement
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 2After 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 0Every 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 manymemcpyandstrcpyimplementations. - **Reading a FIFO register:**
*buf++ = *UART_DR;stores the register value and advances the buffer pointer. HereUART_DRwould be avolatilepointer 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:**
*++cursorskips ahead before reading the next token character.
Common Mistakes and Troubleshooting
| Mistake | What happens | Fix |
|---|---|---|
Writing *p++ when (*p)++ was meant | The pointer moves and the data is unchanged | Add parentheses and read the line aloud |
Writing ++*p in a loop expecting traversal | The same element is incremented repeatedly | Use p++ or *p++ to advance |
Reading *--p when p already sits on the first element | Reads before the array start | Start from one past the end, or test p > start first |
Modifying the same variable twice in one expression, such as *p++ = *p | Unspecified order of evaluation, undefined behaviour in some forms | Split into separate statements |
Using *p++ on a const table and assigning through it | Compile error on write | Use 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
volatilefor pointers into hardware registers so the compiler does not remove or reorder the reads. - Run your code under
-fsanitize=undefined,addressduring 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. *++pmoves first and then reads.++*pand(*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