Sequence point
In C and C++, a sequence point defines any point in a computer program's execution at which it is guaranteed that all side effects of previous evaluations will have been performed, and no side effects from subsequent evaluations have yet been performed. They are a core concept for determining the validity of and, if valid, the possible results of expressions. Adding more sequence points is sometimes necessary to make an expression defined and to ensure a single valid order of evaluation.
Documentation for C11 and C++11 stopped using the term "sequence point" and now uses alternative terms:
- An expression's evaluation can be "sequenced before" the evaluation of another expression.
- The expressions' evaluation is "indeterminately sequenced", meaning that one is "sequenced before" the other, but which is unspecified.
- The expressions' evaluation is "unsequenced", meaning the operations in each expression may be interleaved.
a = i++; has undefined behavior.Examples of ambiguity
Consider two functionsf and g. In C and C++, the + operator is not associated with a sequence point, and therefore in the expression f+g it is possible that either f or g will be executed first. The comma operator introduces a sequence point, and therefore in the code f,g the order of evaluation is defined: first f is called, and then g is called.Sequence points also come into play when the same variable is modified more than once within a single expression. An often-cited example is the C expression
i=i++, which apparently both assigns i its previous value and increments i. The final value of i is ambiguous, because, depending on the order of expression evaluation, the increment may occur before, after, or interleaved with the assignment. The definition of a particular language might specify one of the possible behaviors or simply say the behavior is undefined. In C and C++, evaluating such an expression yields undefined behavior. Other languages, such as C#, define the precedence of the assignment operator and the increment operator in such a way that the result of the expression i=i++ is guaranteed.Behavior
Up to C++03
In C and C++, sequence points occur in the following places.- Between evaluation of the left and right operands of the
&&,||, and comma operators. For example, in the expression, all side effects of the sub-expression are completed before any attempt to access. - Between the evaluation of the first operand of the ternary conditional operator and its second or third operand. For example, in the expression there is a sequence point after the first, meaning it has already been incremented by the time the second instance is executed.
- At the end of a full expression. This category includes expression statements, return statements, the controlling expressions of,,, or - statements, and each of the three expressions in a
forstatement. - Before a function is entered in a function call. The order in which the arguments are evaluated is not specified, but this sequence point means that all of their side effects are complete before the function is entered. In the expression, is called with a parameter of the original value of, but is incremented before entering the body of. Similarly, and are updated before entering and respectively. However, it is not specified in which order,, are executed, nor in which order,, are incremented. If the body of accesses the variables and, it might find both, neither, or just one of them to have been incremented.
- At a function return, after the return value is copied into the calling context.
- At the end of an initializer; for example, after the evaluation of in the declaration.
- Between each declarator in each declarator sequence; for example, between the two evaluations of in.
- After each conversion associated with an input/output format specifier. For example, in the expression, there is a sequence point after the is evaluated and before printing.
C11 and C++11
C++11 brought diverse changes, including features that improve the language's performance, usability, and multithreading. Partially because of the introduction of language support for threads, C11 and C++11 introduced new terminology for evaluation order. An operation may be “sequenced before” another, the two can be “indeterminately sequenced”, or the two can be “unsequenced”.C++17
C++17 restricted several aspects of evaluation order. The expression will always perform the memory allocation before evaluating the constructor arguments. The operators,,,,, and the subscript and function call operator are guaranteed to be evaluated left to right. For example, the codestd::cout << a << b << c; // parsed as ) << b) << c);
is newly guaranteed to call,, and in that order. The right-hand side of any assignment-like operator is evaluated before the left-hand side, so that
b *= a; is guaranteed to evaluate first. Finally, although the order in which function parameters are evaluated remains implementation-defined, the compiler is no longer allowed to interleave sub-expressions across multiple parameters.