What is This? Why do I Want It? What Does it Do?

Sometimes I turn off my programmer brain and look at code to admire it in an innocent way. At first glance a C program source code file looks poetic, using the same patterns and flow. Code is also cryptic, which inspires many programmers to try to invent a new way to do something in a charming and confusing manner. One of my attempts was to rationalize this expression: ++a++

At first, I’m delighted with the symmetry of ++a++. It’s pretty cool. But what would it do? How would the ++ (increment) operator bind to the variable?

The order of operations for ++ is right to left. So in my frontal lobe, I figured that a++ would happen first, adding one to the value stored in variable a. Then this value would be incremented again. Neat but improper, as the compiler informed me when building this code:

2024_08_03-Lesson.c

#include <stdio.h>

int main()
{
    int a = -1;

    printf("%d\n",++a++);

    return 0;
}

Here is the output:

2024_08_03-Lesson.c:7:16: error: expression is not assignable
        printf("%d\n",++a++);
                      ^ ~~~
1 error generated.

Without knowing the specifics, my guess is that the compiler performs the a++ operation first. It translates the expression into the value of variable a plus one, which would be zero. Then the code reads the expression as ++0, which is the “non-assignable” part: You cannot increment a result.

Or it could work the other way with ++a happening first, which again would be zero, and then trying to increment zero. Either way, the expression — which still looks cool — is invalid in C.

Using parentheses doesn’t help: (++a)++ or ++(a++) yields the same error. These repeat the same illogic of incrementing a result.

The resolution is to figure out what the code is wanting to do. If you replace the bogus expression with (++a)+1, negative one is incremented to zero, then one is added. The result is one. The same result appears if you code it as 1+(a++), which can also be written without parentheses: 1+a++. This is the original intent of the ++a++ expression, so either way works.

I shall continue to strive to locate interesting and pretty things in C programming to see how they work — or not. Sometimes programming has more to do with appearance than practicality, which is probably the motivation behind the entire obfuscated C movement.

6 thoughts on “What is This? Why do I Want It? What Does it Do?

  1. Really interesting stuff you (again) came up with!

    While reading this, I immediately thought to myself that this cannot possibly be valid C code because as noted in the C standard

    “Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.”

    Itʼs the first sentence within this paragraph thatʼs putting the above into the realm of “undefined behavior”. I think at least mentioning the concept of sequence points would have been helpful here.

    That being said… having done some further research it seems that the given example also illustrates one of the lesser known differences between C and C++.

    Specifically, the following is of importance:
    Why are multiple pre-increments allowed in C++ but not in C?

    • In C++, “(++a)++” compiles because the standard says “The operand of prefix ++ is modified by adding 1. The operand shall be a modifiable lvalue.”

    • In C, “(++a)++” doesnʼt compile because the standards says “The value of the operand of the prefix ++ operator is incremented. The result is the new value of the operand after incrementation.” (The explanation you gave.)

    Additionally, it seems to be the case that within C++ the rules changed over time:

    • Before C++11, “(++a)++” resulted in “undefined behavior” (as explained here).

    • Since C++11, “(++a)++” exhibits the expected behavior (as explained here).
    … as referenced in this SO answer, the arguments for this behavioral change can be found in defect reports DR 637 and DR 222.

    In short:

    gcc -std=cXY 2024_08_03-Lesson.c    // wonʼt compile

    g++ -std=c++03 2024_08_03-Lesson.c  // compiles but undefined behavior
    g++ -std=c++11 2024_08_03-Lesson.c  // valid and works as expected

    In any case, this only pertains to the pre-increment operator. The result of a post-increment is always a (unmodifiable) rvalue—in C and C++ both.

    Phew, that was exhausting… all this language lawyering surely is tiresome 😉

  2. I’m glad you brought up this issue. It’s one of the reasons I focus exclusively on C and purposefully avoid C++.

    I see far too many C programmers who straddle the line, which is okay. But if I’m truly to teach C, I need to focus on it specifically. No cross pollination here!

    Thank you again for the explanation. It’s amazing that it works in C++.

  3. I agree with the staying focused part of your reply, especially for beginners of the language. Put simply, the main takeaway to remember probably is:

    Donʼt try to modify an “object” — Standardeese for what one would normally refer to as a variable — multiple times during a single expression, thatʼs a big no-no.

    That even though it happens to work since C++11, if one is using a C++ compiler.

  4. How crazy can you get in C++? If this trick worked in C, I’d keep expanding it into some monster like this: (++((++(a++))++))++ and so on.

  5. No, your example wouldnʼt compile—to clarify:

    Even in C++, only the pre-increment operator returns an lvalue, the post-increment operator behaves like in C. (Because the post-increment operator has to return the previous value while a pre-increment doesnʼt.)

    Thus, one can stack as many ++ pre-increments as one likes, put that in parentheses and then, optionally, perform a single post-increment… after that itʼs the end of the line though.

    For example, the following compiles and is valid:

    (++++++a)++

    Because pre-increment ++ has right-to-left associativity, the compiler will implicitly set parentheses like so:

    ((++(++(++a)))++)

Leave a Reply