En C y C++ , un punto de secuencia define cualquier punto en la ejecución de un programa informático en el que se garantiza que se habrán realizado todos los efectos secundarios de las evaluaciones anteriores y que aún no se ha realizado ningún efecto secundario de las evaluaciones posteriores. Son un concepto fundamental para determinar la validez de las expresiones y, si son válidas, los posibles resultados de las mismas. A veces es necesario añadir más puntos de secuencia para definir una expresión y garantizar un único orden válido de evaluación.
Con C11 y C++11 , el término punto de secuencia ha sido reemplazado por el de secuenciación. Existen tres posibilidades: [1] [2] [3]
La ejecución de evaluaciones no secuenciadas puede superponerse, lo que puede dar lugar a un comportamiento indefinido potencialmente catastrófico si comparten el estado . Esta situación puede surgir en cálculos paralelos , lo que provoca condiciones de carrera , pero el comportamiento indefinido también puede dar lugar a situaciones de un solo subproceso. Por ejemplo, a[i] = i++;
(donde a
es una matriz y i
es un entero) tiene un comportamiento indefinido.
Consideremos dos funciones f()
y g()
. En C y C++, el +
operador no está asociado a un punto de secuencia y, por lo tanto, en la expresión f()+g()
es posible que o f()
se g()
ejecute primero. El operador coma introduce un punto de secuencia y, por lo tanto, en el código f(),g()
se define el orden de evaluación: f()
se llama a primero y g()
se llama a después.
Los puntos de secuencia también entran en juego cuando la misma variable se modifica más de una vez dentro de una sola expresión. Un ejemplo que se cita a menudo es la expresión de Ci=i++
, que aparentemente asigna i
su valor anterior e incrementa i
. El valor final de i
es ambiguo, porque, dependiendo del orden de evaluación de la expresión, el incremento puede ocurrir antes, después o intercalado con la asignación. La definición de un lenguaje en particular puede especificar uno de los comportamientos posibles o simplemente decir que el comportamiento es undefined . En C y C++, la evaluación de una expresión de este tipo produce un comportamiento undefined. [4] Otros lenguajes, como C# , definen la precedencia del operador de asignación e incremento de tal manera que el resultado de la expresión i=i++
está garantizado.
En C [5] y C++, [6] los puntos de secuencia aparecen en los siguientes lugares. (En C++, los operadores sobrecargados actúan como funciones y, por lo tanto, los operadores que han sido sobrecargados introducen puntos de secuencia de la misma manera que las llamadas a funciones).
&&
( AND lógico ), ||
( OR lógico ) (como parte de la evaluación de cortocircuito ) y coma . Por ejemplo, en la expresión , todos los efectos secundarios de la subexpresión se completan antes de cualquier intento de acceder a .*p++ != 0 && *q++ != 0
*p++ != 0
q
a = (*p++) ? (*p++) : 0
*p++
a=b;
if
switch
while
do
while
for
f(i++) + g(j++) + h(k++)
f
i
i
f
j
k
g
h
f()
g()
h()
i
j
k
f
j
k
f(a,b,c)
a
b
c
5
en la declaración .int a = 5;
a++
int x = a++, y = a++
printf("foo %n %d", &a, 42)
%n
42
En parte debido a la introducción del soporte de lenguaje para subprocesos, C11 y C++11 introdujeron una nueva terminología para el orden de evaluación. Una operación puede ser "secuenciada antes" de otra, o las dos pueden ser secuenciadas "indeterminadamente" (una debe completarse antes que la otra) o "no secuenciadas" (las operaciones en cada expresión pueden estar intercaladas).
C++17 restringió varios aspectos del orden de evaluación. La new
expresión siempre realizará la asignación de memoria antes de evaluar los argumentos del constructor. Se garantiza que los operadores <<
, >>
, .
, .*
, ->*
, y el operador de llamada de subíndice y función se evaluarán de izquierda a derecha (ya sea que estén sobrecargados o no). Por ejemplo, el código
std :: cout << a () << b () << c (); // analizado como (((std::cout << a()) << b()) << c());
Se garantiza que se llamará a a
, b
y c
en ese orden. El lado derecho de cualquier operador de tipo asignación se evalúa antes que el lado izquierdo, por lo que b() *= a();
se garantiza que se evaluará a
primero. Finalmente, aunque el orden en el que se evalúan los parámetros de la función sigue estando definido por la implementación, ya no se permite que el compilador intercale subexpresiones en varios parámetros. [9]