Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this (i = ++i % 3) generate a warning: "may be undefined"? [duplicate]

Tags:

c

int main(void)
{
    int i = 0;
    i = ++i % 3;
    return 0;
}

I compile it like this:

$ gcc -Wall main.c -o main
main.c: In function ‘main’:
main.c:4: warning: operation on ‘i’ may be undefined

Why does the compiler say i may be undefined?

like image 535
vv1133 Avatar asked Oct 24 '11 14:10

vv1133


3 Answers

Because you are modifying the value of i twice without an intervening sequence point. It's undefined behavior.

like image 66
Fred Larson Avatar answered Sep 22 '22 07:09

Fred Larson


In standardese, it's undefined behaviour because i is modified twice without an intervening sequence point.

i = ++i % 3;

But that's not really the point. The real point is: why on earth would anyone write such code?!

What is the value that you want i to have? If you're assigning a whole new value into i with i = ..., what effect are you trying to achieve with ++i? If this were parallel-universe-C in which code like this actually had meaning, then -- in the best case -- the incremented i is immediately replaced with the whole new value assigned. So why not just write it as

i = (i+1) % 3;

which is also correct in C-as-we-know-it.

like image 38
John Marshall Avatar answered Sep 25 '22 07:09

John Marshall


As others have pointed out, the behavior is undefined:

6.5 Expressions
...
2 Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression.72) Furthermore, the prior value shall be read only to determine the value to be stored.73)
...
72) A floating-point status flag is not an object and can be set more than once within an expression. 73) This paragraph renders undefined statement expressions such as
    i = ++i + 1;
    a[i++] = i;
while allowing
    i = i + 1;
    a[i] = i;

The expression i = ++i % 3 attempts to modify the value contained in i twice before the next sequence point (in this case, the ; ending the statement), once by evaluating ++i, and once by evaluating the larger assignment expression.

Now, why would this be a problem? After all, C# and Java can handle these expressions just fine.

The problem is that, with few exceptions, C doesn't guarantee that operands in an expression are evaluated in any particular order, or that the side effects of an expression will be applied immediately after the expression is evaluated (unlike C# and Java, which do make those guarantees). For example, the expression ++i has a result (i + 1) and a side effect (increment the value stored in i); however, the side effect can be deferred until the larger expression has been evaluated. IOW, the following sequence of actions is allowed:

    t0 = i + 1
    t1 = t0 % 3
    i = t1
    i = i + 1

Oopsie. Not what we wanted.

This was a deliberate design decision; the idea is that it allows compilers to reorder evaluations in an optimal manner (by taking advantage of a value that's already in a register, say). The downside is that certain combinations of expressions will have unpredictable results.

like image 37
John Bode Avatar answered Sep 21 '22 07:09

John Bode