ENOSUCHBLOG

Programming, philosophy, pedaling.


Mini-post: ++x++

Feb 18, 2015     Tags: programming    

This post is at least a year old.

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.

So why does ++*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'" */

Concluding notes

++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.