In today's article we are going to delve into the topic of Sequence point and discover all the facets and aspects that surround it. Throughout the next lines we will explore from its origins to its most current applications, we will analyze its impact on society and its relevance in different fields. In addition, we will delve into the opinions and points of view of experts in the field, as well as personal experiences that will allow us to better understand the importance and relevance of Sequence point today. This article will serve as a complete guide for those who wish to understand this topic further and discover all the possibilities it offers.
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.
With C11 and C++11, usage of the term sequence point has been replaced by sequencing. There are three possibilities:[1][2][3]
The execution of unsequenced evaluations can overlap, leading to potentially catastrophic undefined behavior if they share state. This situation can arise in parallel computations, causing race conditions, but undefined behavior can also result in single-threaded situations. For example, a = i++;
(where a
is an array and i
is an integer) has undefined behavior.
Consider two functions f()
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.[4] Other languages, such as C#, define the precedence of the assignment and increment operator in such a way that the result of the expression i=i++
is guaranteed.
In C[5] and C++,[6] sequence points occur in the following places. (In C++, overloaded operators act like functions, and thus operators that have been overloaded introduce sequence points in the same way as function calls.)
&&
(logical AND), ||
(logical OR) (as part of short-circuit evaluation), and comma operators. For example, in the expression *p++ != 0 && *q++ != 0
, all side effects of the sub-expression *p++ != 0
are completed before any attempt to access q
.a = (*p++) ? (*p++) : 0
there is a sequence point after the first *p++
, meaning it has already been incremented by the time the second instance is executed.a=b;
), return statements, the controlling expressions of if
, switch
, while
, or do
-while
statements, and each of the three expressions in a for
statement.f(i++) + g(j++) + h(k++)
, f
is called with a parameter of the original value of i
, but i
is incremented before entering the body of f
. Similarly, j
and k
are updated before entering g
and h
respectively. However, it is not specified in which order f()
, g()
, h()
are executed, nor in which order i
, j
, k
are incremented. If the body of f
accesses the variables j
and k
, it might find both, neither, or just one of them to have been incremented. (The function call f(a,b,c)
is not a use of the comma operator; the order of evaluation for a
, b
, and c
is unspecified.)5
in the declaration int a = 5;
.a++
in int x = a++, y = a++
.[8] (This is not an example of the comma operator.)printf("foo %n %d", &a, 42)
, there is a sequence point after the %n
is evaluated and before printing 42
.![]() | This section needs expansion. You can help by adding to it. (April 2023) |
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, or the two can be "indeterminately" sequenced (one must complete before the other) or "unsequenced" (the operations in each expression may be interleaved).
C++17 restricted several aspects of evaluation order. The new
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 (whether they are overloaded or not). For example, the code
std::cout << a() << b() << c(); // parsed as (((std::cout << a()) << b()) << c());
is newly guaranteed to call a
, b
and c
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 a
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.[9]