Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
C Increment Decrement Operators
Master ++ and — in C: prefix versus postfix, the lvalue rule, loops, pointer idioms and the traps that matter in embedded C programming. Part of our free C tutorial series.
Welcome to the next lecture of our free C programming course on EmbeddedPathashala. In the last lectures you learned the arithmetic and assignment operators. Today we study the two smallest operators in the language, increment and decrement, which drive nearly every loop and pointer walk in real firmware.
This free C tutorial is written for students who have never seen the idea of “use the value first, change it later”. We rebuild each idea from first principles, show runnable code with real output, and finish with a quiz and interview questions. If you are aiming at embedded C programming jobs, this is one of the topics interviewers love to test.
What You Will Learn
- What
++and--do and why they need a variable - The difference between prefix and postfix, with timelines
- How to trace
y = x++andy = ++xby hand - Using the operators in loops, string copies and ring-buffer indexes
- How
*p++,(*p)++and*++pdiffer - Why
i = i++is undefined and how GCC warns you - Embedded concerns: overflow, atomicity and
volatile
Key Terms in This Lecture
Prerequisites
Before you start
- Variables and assignment (
=) from the earlier lectures. - The arithmetic operators
+and-. - A C compiler such as GCC:
gcc --versionshould print a version.
Why Increment and Decrement Matter in a Free C Programming Course
Almost every loop you will ever write needs a counter that moves by one: one more byte received, one more array slot visited, one less retry left. C gives you two tiny operators for exactly this job, ++ and --. Because embedded C programming is full of counters, indexes and pointers that walk through memory, these two operators show up in firmware more often than almost any other symbol. In this free C programming course lecture we learn what they do, why each one comes in two flavours, and how to avoid the handful of traps that catch even experienced engineers.
In plain words, ++x means “add 1 to x” and --x means “subtract 1 from x”. They are shorthand for x = x + 1 and x = x - 1. The shorthand is worth learning properly, because the position of the operator (before or after the variable) changes the value the expression hands back to the rest of your statement.
Increment and Decrement Operators in C: The Basics
| Operator | Name | Same as | Operands needed |
|---|---|---|---|
++x or x++ | Increment | x = x + 1 | One modifiable variable |
--x or x-- | Decrement | x = x - 1 | One modifiable variable |
Both are unary operators: they work on a single operand. That operand must be something that has a memory location you can change, such as a variable, an array element or a dereferenced pointer. This is the first rule to remember: these operators work on variables only, never on constants or on the result of a calculation.
So ++5 is meaningless (you cannot change the number five) and ++(x + y + z) is meaningless too (the sum is a temporary value that has no home to store the new number). The compiler refuses both. Here is a small file that breaks the rule on purpose:
int main(void)
{
int a = 2, b = 3;
++5;
(a + b)++;
return 0;
}
Build it and read what GCC says:
gcc -std=c11 -Wall -Wextra ep_bad5.c
ep_bad5.c: In function ‘main’:
ep_bad5.c:4:5: error: lvalue required as increment operand
4 | ++5;
| ^~
ep_bad5.c:5:12: error: lvalue required as increment operand
5 | (a + b)++;
| ^~
The words lvalue required are the key. An lvalue is anything that names a storage location. If you ever see that message, you have tried to modify something that cannot be modified.
Prefix vs Postfix in This Free C Programming Course
Each operator comes in two forms, and the difference is purely about when the new value becomes visible to the surrounding expression:
| Form | Written as | What happens first | Value the expression gives |
|---|---|---|---|
| Prefix | ++x, --x | x is changed | The new value |
| Postfix | x++, x-- | The old value is remembered | The old value |
In both cases the variable ends up changed by exactly one. Only the value that flows into the rest of the statement differs. Look at the timeline for y = ++x and y = x++ when x starts at 3:
Prefix: y = ++x (x starts at 3)
Postfix: y = x++ (x starts at 3)
Memory trick for students
Operator before the variable: change it before you use it. Operator after the variable: use it first, change it after. If you write the operator on its own line (x++; or ++x;), both forms behave identically because nobody uses the value.
Example 1: Seeing Prefix and Postfix Side by Side
This program starts every experiment from the same value so you can compare the results directly.
#include <stdio.h>
int main(void)
{
int x = 3;
int y;
y = ++x; /* prefix: bump first, then copy */
printf("after y = ++x : x = %d, y = %d\n", x, y);
x = 3;
y = x++; /* postfix: copy first, then bump */
printf("after y = x++ : x = %d, y = %d\n", x, y);
x = 3;
y = --x;
printf("after y = --x : x = %d, y = %d\n", x, y);
x = 3;
y = x--;
printf("after y = x-- : x = %d, y = %d\n", x, y);
return 0;
}
gcc -std=c11 -Wall -Wextra -o ep_pre_post ep_pre_post.c
./ep_pre_post
Expected output:
after y = ++x : x = 4, y = 4
after y = x++ : x = 4, y = 3
after y = --x : x = 2, y = 2
after y = x-- : x = 2, y = 3
Read each line against the rule: with the prefix forms y copies the already-changed x; with the postfix forms y copies the old x and the change happens afterwards. The decrement versions behave the same way in the opposite direction.
Example 2: Tracing the Value Inside printf
A very common classroom exercise is to print a variable before and after changing it. Each printf below uses at most one change to n, so the results are well defined:
#include <stdio.h>
int main(void)
{
int n = 8;
printf("start n = %d\n", n);
printf("++n gives %d\n", ++n);
printf("now n = %d\n", n);
printf("n++ gives %d\n", n++);
printf("now n = %d\n", n);
printf("--n gives %d\n", --n);
printf("n-- gives %d\n", n--);
printf("final n = %d\n", n);
return 0;
}
Expected output:
start n = 8
++n gives 9
now n = 9
n++ gives 9
now n = 10
--n gives 9
n-- gives 9
final n = 8
Notice line three and four. n++ prints 9, but the very next line shows 10. The printed value was the old one; the change landed right after it was fetched.
Do not squeeze two changes into one printf
A statement like printf("%d %d", n++, n) reads and changes n in the same expression with no guaranteed order. The C standard calls this undefined behaviour. Keep one change per statement while you are learning.
Increment and Decrement in Loops: Free C Programming Course Examples
The most common home for these operators is a loop counter. In a for loop the third part runs after each pass, and its value is thrown away, so i++ and ++i do the same job there.
#include <stdio.h>
int main(void)
{
int i;
int sum_post = 0, sum_pre = 0;
for (i = 1; i <= 5; i++)
sum_post += i;
for (i = 1; i <= 5; ++i)
sum_pre += i;
printf("sum with i++ : %d\n", sum_post);
printf("sum with ++i : %d\n", sum_pre);
i = 10;
while (i-- > 7)
printf("loop body sees i = %d\n", i);
printf("after loop i = %d\n", i);
return 0;
}
Expected output:
sum with i++ : 15
sum with ++i : 15
loop body sees i = 9
loop body sees i = 8
loop body sees i = 7
after loop i = 6
The while (i-- > 7) loop deserves a slow read. The test uses the old value of i (10, then 9, then 8), compares it with 7, and only afterwards decrements. By the time the loop body prints, i is already one lower, which is why the body sees 9, 8, 7. When the test finally compares 7 with 7 and fails, the decrement still happens, leaving i at 6. This “off by one after the loop” behaviour is a classic source of bugs, so trace it by hand until it feels natural.
Pointers and Buffers: Where Postfix Shines
In embedded C programming you constantly walk through a buffer one element at a time. Postfix lets you use the current element and step forward in the same breath. The string-copy idiom below is a real pattern found in many firmware code bases:
#include <stdio.h>
static void ep_copy(char *dst, const char *src)
{
while ((*dst++ = *src++) != '\0')
; /* copies the terminating '\0' too */
}
int main(void)
{
char buf[16];
int rx_count = 0;
int i;
ep_copy(buf, "UART1");
printf("copied text : %s\n", buf);
for (i = 0; buf[i] != '\0'; i++)
rx_count++;
printf("length seen : %d\n", rx_count);
{
int head = 0;
int k;
printf("ring index :");
for (k = 0; k < 7; k++) {
printf(" %d", head);
head = (head + 1) % 4;
}
printf("\n");
}
return 0;
}
Expected output:
copied text : UART1
length seen : 5
ring index : 0 1 2 3 0 1 2
The line *dst++ = *src++ looks cryptic at first. The postfix ++ has a higher precedence than the dereference *, so it parses as *(dst++) = *(src++): copy the character that src points at into the place dst points at, then move both pointers forward by one. The loop stops after the terminating \0 has been copied because the assignment itself evaluates to that copied value, which is zero.
The second half of the program shows a ring-buffer index. Using head = (head + 1) % 4 wraps the index back to 0 after 3. You can write that as head++ followed by a wrap check, but the explicit form keeps the wrap logic visible.
| Expression | Parsed as | Meaning |
|---|---|---|
*p++ | *(p++) | Read the target, then move the pointer on |
(*p)++ | increment the target | Pointer stays, the pointed-to value grows by one |
*++p | *(++p) | Move the pointer first, then read the new target |
++*p | ++(*p) | Increase the pointed-to value, then use it |
Common Mistakes and Troubleshooting
#include <stdio.h>
int main(void)
{
int i = 5;
int r = i++ + ++i; /* do not do this */
printf("%d\n", r);
return 0;
}
Compile it with warnings turned on and GCC speaks up:
ep_ub.c: In function ‘main’:
ep_ub.c:6:19: warning: operation on ‘i’ may be undefined [-Wsequence-point]
6 | int r = i++ + ++i; /* do not do this */
| ^~~
i++ + ++i changes i twice between two points where the language guarantees things have settled. The standard gives no order, so the result may differ between compilers, optimisation levels or even builds. Always turn on -Wall: the compiler catches this class of bug for free.
| Mistake | Why it hurts | Fix |
|---|---|---|
i = i++; | Undefined behaviour: i is written twice with no ordering | Write i++; on its own |
a[i] = i++; | Index may be read before or after the change | Split into two statements |
x++ on a const variable | Compile error: read-only variable | Remove const or use a copy |
Using ++ as +1 in an expression | The variable changes as a side effect and bugs appear later | Write x + 1 when you only need the value |
Forgetting the loop leaves i one off | while (i--) ends with i at -1 | Remember the test happens before the decrement |
Best Practices, Performance and Safety
- Use a standalone
i++;or++i;statement whenever you do not need the value. It is the clearest style. - Never change the same variable twice in one expression, and never read it while changing it elsewhere in the same expression.
- In C the two forms generate identical machine code for plain integers on any modern compiler, so choose for clarity, not speed.
- Beware overflow.
intreaching its maximum and then using++is undefined behaviour. Anunsignedcounter wraps to zero in a well-defined way, which is why embedded timers use unsigned types. - On a microcontroller,
counter++on a variable shared with an interrupt handler is usually compiled into a load, an add and a store. If the interrupt fires in the middle, you lose a count. Protect shared counters with a critical section or an atomic operation. - Mark variables that hardware or an ISR changes as
volatile, but remembervolatiledoes not make++atomic. - Check array bounds yourself:
buf[idx++] = byte;without a limit test is how buffer overflows begin.
Real-world uses you will meet
- Counting received UART bytes or dropped packets.
- Walking a lookup table or a DMA descriptor list with a pointer.
- Retry counters such as
while (retries--). - Circular buffer head and tail indexes in drivers.
Summary and Key Takeaways
++adds one and--subtracts one, and both need a modifiable variable.- Prefix changes first and hands over the new value. Postfix hands over the old value and changes afterwards.
- Alone on a line, prefix and postfix are interchangeable.
*p++reads then advances the pointer;(*p)++changes the pointed-to data.- Changing one variable twice in a single expression is undefined behaviour.
- Turn on
-Wall -Wextraso the compiler spots sequence-point mistakes for you.
Conclusion
You now know the two tiniest operators in C and the single idea that governs them: the operator position decides whether the expression sees the old or the new value. Practise by tracing small snippets by hand before running them, then confirm with the compiler. This habit will save you hours when you debug counters and pointers on real hardware. Next in our free C tutorial series we move from changing values to comparing them with the relational operators.
Practice Quiz: Predict the Output
Work each answer out on paper first, then open the answer.
Q1. With int a = 9, b = 5; what is the value of (a > b) + (a > 8) + (b > 8)? (This one prepares you for the next lecture.)
Show answer
2. The first two comparisons are true (1 each) and the last is false (0).
Q2. After int k = 4, b, c; b = k++; c = ++k; what are k, b and c?
Show answer
k is 6, b is 4 and c is 6. The postfix copies 4 and makes k 5; the prefix then makes k 6 and copies 6.
Q3. Which of these are invalid: 7++, x++, (x + 1)--, --y?
Show answer
7++ and (x + 1)-- are invalid because neither has a storage location to modify. x++ and --y are fine.
Q4. How many times does the body run for int n = 3; while (n--) count++; and what is n afterwards?
Show answer
3 times, and n ends as -1. The test sees 3, 2, 1 as true and 0 as false, but the decrement still happens on the failing test.
Q5. char *p = "AB"; char first = *p++; What are first and *p afterwards?
Show answer
first is A and *p is B. The pointer was advanced after the read.
Q6. unsigned char uc = 255; uc++; What does uc hold?
Show answer
0. Unsigned types wrap around in a well-defined way.
Q7. True or false: ++x is always faster than x++ in C.
Show answer
False. For ordinary integer variables compilers produce the same code. Choose for readability.
Q8. Is i = i++ + 1; safe to use?
Show answer
No. i is both modified by the increment and assigned in the same expression with no ordering, so the behaviour is undefined.
Interview Questions on ++ and —
Q1. What is the difference between x++ and ++x?
Show answer
Both increase x by one. x++ evaluates to the old value, ++x evaluates to the new value. The difference matters only when the result is used.
Q2. Why is ++5 a compile error?
Show answer
The operand must be an lvalue (a modifiable object). A literal constant has no storage to update.
Q3. Explain *p++, (*p)++ and *++p.
Show answer
*p++ reads the current target then advances the pointer. (*p)++ increments the data being pointed at. *++p advances first and then reads the new target.
Q4. Is counter++ atomic on a microcontroller?
Show answer
Usually not. It typically compiles to a load, an add and a store, so an interrupt in the middle can corrupt the update. Use a critical section or an atomic primitive.
Q5. What happens when you increment a signed int past INT_MAX?
Show answer
Signed overflow is undefined behaviour in C. Unsigned overflow wraps modulo 2^N.
Q6. Why is i = i++ + ++i a bug?
Show answer
It modifies i more than once without a sequence point, so the C standard gives no defined result.
Q7. Why do for loops usually use i++ in the third clause?
Show answer
The result is discarded, so any form works. i++ is a convention that reads naturally.
Frequently Asked Questions About Increment and Decrement in C
What does ++ do in C?
It adds one to a variable. Written before the variable (++x) it hands over the new value; written after (x++) it hands over the old value.
Can I use ++ on a constant or an expression like (a + b)?
No. The operand must be a modifiable variable. The compiler reports lvalue required as increment operand.
Is there a speed difference between i++ and ++i in C?
For plain integer variables modern compilers generate the same machine code, so pick the one that reads best.
What is the difference between *p++ and (*p)++?
*p++ uses the pointed-to value and moves the pointer. (*p)++ leaves the pointer where it is and increases the value it points to.
Why does i = i++ give strange results?
It writes i twice in one expression with no defined order, which is undefined behaviour in C.
Where can I learn C operators for free?
EmbeddedPathashala runs a free C programming course for embedded systems with compiled examples, quizzes and interview questions for every topic.
Is this free C programming course suitable for beginners?
Yes. Each lecture starts from the basics and builds to the embedded C programming patterns used in real firmware.
Next: Relational Operators in C
Continue the free C programming course with the six relational operators: how comparisons produce 1 or 0 and why = versus == is the most famous bug in C.
Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.

1 Comment