2

I have this line of code:

front = (++front) % size;

In C I get no warnings but in C++ I get the warning operation on front may be undefined [-Wsequence-point]. How does this preincrement usage cause undefined behavior? In my mind, this line is very unambiguous and will be interpreted as:

  1. increment front
  2. mod front with size
  3. assign new value to front.

Is my compiler just throwing a blanket warning?

P.S. I understand the warning if I were doing something like front = front++; or Heaven forbid front = front++ + front++;.

EDIT: This warning was produced in CodeBlocks on Windows 64 using GCC (tdm-1) 4.6.1

thndrwrks
  • 1,644
  • 1
  • 17
  • 29
  • 1
    front++ vs ++front doesn't change anything here, the left side of the equal sign could be the post or pre incremented value depending on which side is evaluated first. AFAIK it's not obligated to evaluate left side of = before right side. – Borgleader Oct 30 '14 at 18:33
  • I think thndrwrks question rests on the idea that the assignment can't occur until after the `%` and the `%` can't happen until after the `(++front)`, which is bracketed in order to give the `++` precedence, so there is therefore prima facie no sequencing problem. Though clearly that's not the case. I'm not currently well-versed enough in the spec to be able to give a definitive answer but maybe someone could speak to that? – Tommy Oct 30 '14 at 18:37
  • Mate. Search before posting. – Lightness Races in Orbit Oct 30 '14 at 18:44
  • 1
    I understand and looked at that particular post but it is still not clear to me why this would produce a warning in C++ but not in C. EDIT: in fact that answer even states "`5) i = ++i + 1 ; //well defined behaviour`" which is similar to my line of code. So why am I getting a warning? – thndrwrks Oct 30 '14 at 18:50
  • @thndrwrks, Keep in mind that answer is only for C++11. GCC still diagnoses it nonetheless. – chris Oct 30 '14 at 18:54
  • voting to reopen, the linked duplicate doesn't address OP's specific line (and I haven't seen any question that properly addresses `i = ++i;` either) – M.M Oct 30 '14 at 19:11
  • @thndrwrks can you specify a tag for which language you are using? C and C++ have different sequencing rules; questions like this with multiple tags turn into a mess quickly. – M.M Oct 30 '14 at 19:13
  • http://stackoverflow.com/questions/1860461/why-is-i-i-1-unspecified-behavior addresses why it's undefined in C++, though does not discuss C. – Mooing Duck Oct 30 '14 at 19:22
  • 2
    Why do you even want to do it this way? Why modify `i` twice? Just do `front = (front + 1) % size;`. – Derek Ledbetter Oct 30 '14 at 19:22
  • @Matt McNabb I am porting code from C to C++ so this is technically a C++ question I guess. – thndrwrks Oct 30 '14 at 20:19

4 Answers4

5

You are changing front twice between sequence points: once through ++, and once through assignment. This is undefined behaviour.

4

In C++11 this is well-defined; the structure is the same as that of:

i = ++i + 1;

which is given as an example of well-defined behaviour in the Standard itself. For a more detailed explanation see AndreyT's answer here.

In C++03, C89 and C99 this is undefined behaviour as they had looser sequencting rules for ++i.

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365
  • I don't think this is completely black-and-white, as the standard underspecifies exactly when lvalue-to-rvalue conversion occurs, so I suppose discussion surrounding this sort of expression will continue for the foreseeable future :) – M.M Oct 30 '14 at 19:24
  • I bet they will fix this issue in C++11. Having your program well-defined or not just by switching compilation mode - that's very dangerous game. – Slava Oct 30 '14 at 19:26
  • 2
    @Slava we are talking about C++11 (and AFAIK it didn't change in C++14). Having C++11 programs break when switching to an older standard is "OK", it's going the other way that is something we should avoid – M.M Oct 30 '14 at 19:38
  • @MatMcNabb if program would fail to compile in older standard it would be "OK", in this situation it would produce hard to catch buggy behavior. This is definatly not "OK". – Slava Oct 30 '14 at 20:01
  • @Slava: The rules for sequencing _had_ to be specified more precisely to standardise threading. And all standard revisions reduced undefined behaviour (the other way round would be bad, of course, and should exist only where it's really needed). – mafso Oct 30 '14 at 20:10
  • @mafso comittee spends a lot of effort to make old code compilable under new standard. But this issue much worse, I can fix compilation error, but to catch legal code becoming UB just by providing different compiler or the same with different compilation keys, this is unacceptable. – Slava Oct 30 '14 at 20:16
3

Writing the incremented value back to front can happen at any time (before or after the assignment modifies front), so the warning is valid and the code is unsafe.

Alan Stokes
  • 18,815
  • 3
  • 45
  • 64
  • Is this behavior different in C than in C++? – thndrwrks Oct 30 '14 at 18:44
  • Not as far as I know, but my knowledge of C is out of date. – Alan Stokes Oct 30 '14 at 18:57
  • That's interesting - I suspect traditionally, the code would have always been evaluated right-side first with the increment completing before assigning to the left side. But, perhaps not... Isn't this something that only compiler optimization should impact? (i.e. without optimization, doesn't the generated code get strict ordering?) – ash Oct 30 '14 at 19:36
3

The old sequencing rules had edge cases where order of operations was well defined but which were still technically undefined behavior. With C++11 and C11 this has been fixed by replacing the sequence point requirements with 'sequenced-before' and 'sequenced-after' relations.

Your example happens to be such a case. If you're getting warnings in a C11 or C++11 mode then the warning simply hasn't been updated for the new rules yet. In earlier C and C++ modes the warning is correct. If you're not getting warnings in earlier modes then they simply weren't implemented, and that's okay as 'no diagnostic is required'.


At the same time, this line can be written more clearly and also be correct under the old rules:

front = (front + 1) % size;
bames53
  • 86,085
  • 15
  • 179
  • 244