Feb 18, 2015 Tags: programming
WARNING: Simple things ahead. This isn’t me trying to make a complex subject out of a simple topic, just a quick post on why something behaves the way it does.
During a recent IRC session:
1
<redacted>: ++*x++ not legal in C
While incorrect (++*x++
is, in fact, legal and valid C), this set off another
conversation: Why does ++x++
not work?
As it turns out, the answer is pretty simple. In C (and most other languages), mathematic expressions always evaluate into rvalues. For those unfamiliar with the concept of rvalues and lvalues, the principle is simple: lvalues are identifiers, or objects that persist beyond the current expression while rvalues are temporary values that only exist within the expression.
Because ++x
is a mathematical expression, it evaluates (changing the state of x
)
and is then represented as a bare rvalue.
For example:
1
2
int x = 5, y = 0;
y = (++x); /* both x and y are now 6 */
This works because the compiler evaluates the increment and substitutes the expression for the value produced.
This, then, explains why we cannot prefix and postfix an identifier within the same expression. If the compiler substitutes the prefix expression for an rvalue, it sees something like this when it gets to the postfix (comments added for clarity):
1
2
int x = 5;
6++; /* reality: ++x++; */
Because ‘6’ is not a valid identifier, the compiler chokes and spits out something like this:
1
t2.c:16:3: error: lvalue required as increment operand
In other words, the compiler cannot increment ‘6’, because ‘6’ is not a variable or another accessible, persisting (l)value.
++*x++
work?The answer is simple. Because a pointer is being dereferenced, the order of
operations changes. First, x
is dereferenced. Then, the postfix increment
takes effect, bumping the value referenced by x
by 1. Finally, the prefix
increment occurs, bumping the pointer an appropriate number of bytes forward.
The end result is that the following statements are equivalent:
1
2
++*x++;
++(*x++);
A simple example:
1
2
3
4
5
6
char str[128] = "my string";
char *x = str;
++*x++; /* increments *x (str[0]) to 'n', and moves x forward one byte */
printf("*x: '%c', str: '%s'\n", *x, str); /* Output: "*x: 'y', str: 'ny string'" */
++x++
is a pretty funky construction, and I (personally) would be hard pressed
to find a use for it even if it was legal.
++*x++
is also pretty funky, but it does have a potential use case as a terse
way to iterate and increment:
1
2
3
4
5
6
7
char str[4] = "abc";
char *x = str;
while(*x) /* only for NULL-terminated sequences */
++*x++;
printf("%s\n"); /* Output: "bcd" */
Thank you for reading!
- William
P.S.: Try this kind of operation in other languages with prefix/postfix increment operators. The error messages can be amusing compared to C’s (admittedly short and possibly esoteric) message.