Undefined Behaviour

Posted on 7 May, 2018 by Eric Bailey
Tags: c, compilers, school, facepalm

A friend at work today was telling me about his wife's C++ class, wherein the students were asked to predict the behaviour of the following snippet.

x = 10;
y = ++x + --x;

For simplicity, let's assume the type of both x and y is int.

int x, y;

We can inspect the final state of x and y with a simple printf call.

Now, let's see how different compilers handle our program.

#include <stdio.h>


int main(int argc, char **argv)
{
    x = 10;
    y = ++x + --x;

    printf("x = %d, y = %d\n", x, y);
}

At first glance, I would expect the right-hand side of the initialization of y to evaluate to 11 + 10, i.e. 21.

gcc (GCC) 7.3.0

incdec.c: In function ‘main’:
incdec.c:9:9: warning: operation on ‘x’ may be undefined [-Wsequence-point]
     y = ++x + --x;
         ^~~

x = 10, y = 20

Unsurprisingly, GCC computes the final value of x to be 10, the same as its initial value, after being incremented and decremented. It's markedly less intuitive to me that the final value of y is 20.

Clang, on the other hand, agrees with me, i.e. computes the final value of y to be 21.

clang version 5.0.1 (tags/RELEASE_501/final)

incdec.c:9:9: warning: multiple unsequenced modifications to 'x' [-Wunsequenced]
    y = ++x + --x;
        ^     ~~
1 warning generated.

x = 10, y = 21

Both compilers warn about unsequenced modifications and the resultant undefined behaviour. Digging around a bit leads to some fascinating discussions. The GCC team maintains a list of further reading, including a few formal models and analyses of sequence points in C. Further exploration is left as an exercise for the reader.

tl;dr (from https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html)

It is not specified when between sequence points modifications to the values of objects take effect.

The standard is worded confusingly, therefore there is some debate over the precise meaning of the sequence point rules in subtle cases.