Where do sequence points come from?
Asked Answered
E

4

9

I know that writing something like

++a = a++;

Is not only unreadable but also violates the c/c++ sequence points.

Where do these limitations come from? How can one see those 'problems' before finding them as bugs?

Enshrine answered 25/6, 2012 at 17:47 Comment(4)
Well, still, is it a language design limitation or rather a machine (cpu) limiation, common to any language?Enshrine
error: operation on 'a' may be undefined [-Werror=sequence-point] - Ah, thank you, handy compiler.Juglandaceous
Turn on all your warnings and compile and treat warnings as the logical errors that they are and you will not need to worry about this: -Werror -Wall -Wextra -ansi -pedanticChantellechanter
@ArturMarianek: It is a language problem. The language could decide a particular order of evaluation and the compiler would have to enforce that. This is quite unrelated to the hardware.Avram
L
9

Basically there is a C++03 sequence point between each statement. For more information see the SO C++ FAQ. For even more information do consult the C++ standard, and keep in mind that in the C++11 standard sequence points were replaced with sequenced before and sequenced after relations.

To avoid problems, simply don't try to be too clever about doing a lot in each expression.

Don't try to do the compiler's job: leave that to the compiler. Your job is to write code that other humans can easily understand, i.e. clear code. Multiple updates and needless use of operators with side effects is not compatible with that.

Tip: sprinkle const just about everywhere possible.

That constrains the possible state changes that a reader must take into account.

Legofmutton answered 25/6, 2012 at 17:55 Comment(1)
Thank you for the answer. I wish I could 'accept' many answers here ;)Enshrine
O
8

They come from the C or C++ standard, which effectively lists sequence points.1 In a few simple cases, your compiler may be able to warn you that you're invoking undefined behaviour, but not in the general case.

However, you're generally only ever violating sequence-point requirements if you're writing "interesting" code such as your example. The language standard could impose particular constraints on code such as this (which is what languages such as Java do), but there's not a great deal of upside, and the potential downside of preventing certain types of optimisation.


1. The terminology has changed slightly in C++11, but the principle is still largely the same, I think.
Odyl answered 25/6, 2012 at 17:49 Comment(2)
Thank you for the answer. I wish I could 'accept' many answers here ;)Enshrine
I answered a question a while ago about the differences between C++98 and C++11 on this: https://mcmap.net/q/225403/-in-which-versions-of-the-c-standard-does-quot-i-10-10-quot-have-undefined-behaviourRomanticism
H
5

How can one see those 'problems' before finding them as bugs?

Compile your program with strictest level and enable the settings for all warnings to be pointed out as errors. Most mainstream compilers do point out Undefined Behavior errors due to sequence points.

With gcc you can use:

-Wsequence-point

which shall point out sequence point problems. Note that it is enabled by default if you use -Wall.

Of course, the best way is to try and write more readable code which avoids sequence point mis-adventures.

Histoid answered 25/6, 2012 at 17:49 Comment(3)
@chris: -Wall enables it by default, as I already mentioned.Histoid
Thank you for the answer. I wish I could 'accept' many answers here ;)Enshrine
Also turned on automatically when you use: -WallChantellechanter
L
-1

Where do these limitations come from? How can one see those 'problems' before finding them as bugs?

They come from ambiguity in (or freedom to modify) the order of operations during execution.

For your example:

++a = a++;

the language leaves undefined whether the pre-increment on the lvalue should occur before or after the post-increment on the rvalue. Adding a constraint here would incur significant costs; what is the general benefit? What is the clear, obvious way that this example code 'should' behave?

To boost performance, the order of operations in a program may be changed (within strict limits) by the compiler and/or the processor.

Overly constraining execution order would:

  • Make it more difficult to implement C/C++ for different target architectures
  • Add complexity to the language specification
  • Cut performance (in return for what gain?)
  • Prevent many compile-time optimizations, cutting performance
  • Require disabling pipelining and hardware reordering during execution, again cutting performance
  • Perhaps break existing code

Let's look at constraining the order of execution for a different code sample:

a = b++ + ++c - --d - e--;

Suppose that only a limited number of registers are available, and that some of the variables ('d' and 'e') are in registers and some are not. Strictly constraining execution order (let's say left to right) might require:

  • Discard 'd' from registers
  • Discard 'e' from registers
  • Load 'b'
  • Save its initial value as 'b,original'
  • Increment 'b' (might be non-trivial for some data types)
  • Store modified 'b'
  • Reload 'b,original'
  • Load 'c'
  • Increment 'c'
  • Save updated 'c'
  • Add 'b,original' + 'c' and save as partial result
  • etc. etc.

While if allowed to e.g. process 'd' and 'e' first and to increment 'b' at a slightly later time, the compiler may be able to significantly reduce the number of steps and boost performance.

Labyrinth answered 9/11, 2018 at 22:20 Comment(3)
The code is undefined behaviour which means anything whatsoever may happen. It is not "undefined whether [option 1] or [option 2] happens".Queensland
@Queensland yes, we and the compiler all agree that the OP's code example would have undefined behavior. However what the OP asked was, "How can one see those 'problems' before finding them as bugs?" My intention was to help the reader understand why this is undefined behavior, why execution sequences are not specified in detail, because that understanding leads to being able to see where execution may be reordered and the ability to spot such issues.Labyrinth
I would recommend changing your sentence that includes "undefined whether [option 1] or [option 2]". Also a is an lvalue in both cases in ++a = a++, it should not say "post-increment on the rvalue" since in fact a post-increment can only occur on an lvalue (for built-in types).Queensland

© 2022 - 2025 — McMap. All rights reserved.