Articulo de referencia

Punto de secuencia

En C y C++ , un punto de secuencia define cualquier punto de la ejecución de un programa informático en el que se garantiza que se han realizado todos los efectos secundarios de...

En C y C++ , un punto de secuencia define cualquier punto de la ejecución de un programa informático en el que se garantiza que se han realizado todos los efectos secundarios de las evaluaciones anteriores y que aún no se han realizado los efectos secundarios de las evaluaciones posteriores. Son un concepto fundamental para determinar la validez de las expresiones y, si son válidas, sus posibles resultados. En ocasiones, es necesario añadir más puntos de secuencia para definir una expresión y asegurar un único orden de evaluación válido.

La documentación de C11 y C++11 dejó de usar el término "punto de secuencia" y ahora utiliza términos alternativos: "ISO/IEC 14882:2024" ."Una alternativa más detallada a los puntos de secuencia (revisada) (WG21/N2239 J16/07-0099)" . Consultado el 5 de julio de 2012 ."Orden de evaluación" . Consultado el 14 de octubre de 2015 .

  1. La evaluación de una expresión puede realizarse antes que la de otra. (De forma equivalente, la evaluación de la otra expresión puede realizarse después que la de la primera).
  2. La evaluación de la expresión es "secuenciada indeterminadamente", lo que significa que una se "secuencia antes" que la otra, pero no se especifica cuál.
  3. La evaluación de la expresión es "no secuencial", lo que significa que las operaciones en cada expresión pueden intercalarse.

La ejecución de evaluaciones no secuenciadas puede superponerse, lo que puede provocar un comportamiento indefinido potencialmente catastrófico si comparten estado . Esta situación puede surgir en la computación paralela , causando condiciones de carrera , pero el comportamiento indefinido también puede resultar en situaciones de un solo hilo. Por ejemplo, (donde es un array y es un entero) tiene un comportamiento indefinido.a[i]=i++;ai

En estándares C anteriores (como C89 y C99), los puntos de secuencia se definían para aparecer en ubicaciones específicas del código, incluyendo al final de una expresión completa (como una instrucción), después de la evaluación del primer operando de los operadores lógicos AND (&&) y OR (||), después del primer operando del operador condicional (?:) y en los límites de las llamadas a funciones después de que se hayan evaluado todos los argumentos. "Reglas de puntos de secuencia en C y C++" . Consultado el 2 de mayo de 2026 .

Ejemplos de ambigüedad

Consideremos dos funcionesf() y g(). En C y C++, el +operador no está asociado a un punto de secuencia, por lo que en la expresiónf() + g() es posible que se ejecute primero f()o . El operador coma introduce un punto de secuencia, y por lo tanto en el código se define el orden de evaluación: primero se llama a y luego a .g()f(), g()f()g()

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 frecuentemente citado es la expresión Ci=i++ , que aparentemente asigna isu valor anterior e incrementa i. El valor final de ies 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 podría especificar uno de los comportamientos posibles o simplemente decir que el comportamiento es indefinido . En C y C++, evaluar dicha expresión produce un comportamiento indefinido. [ 1 ] Otros lenguajes, como C# , definen la precedencia del operador de asignación y del operador de incremento de tal manera que el resultado de la expresión i=i++está garantizado.

Comportamiento

Hasta C++03

En C [ 2 ] y C++, [ 3 ] los puntos de secuencia aparecen en los siguientes lugares. (En C++, los operadores sobrecargados actúan como funciones, por lo que los operadores sobrecargados introducen puntos de secuencia del mismo modo que las llamadas a funciones).

  1. Entre la evaluación de los operandos izquierdo y derecho de los operadores&& ( 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++!=0q
  2. Entre la evaluación del primer operando del operador condicional ternario y su segundo o tercer operando. Por ejemplo, en la expresión hay un punto de secuencia después del primero , lo que significa que ya se ha incrementado cuando se ejecuta la segunda instancia.a=(*p++)?(*p++):0*p++
  3. Al final de una expresión completa. Esta categoría incluye sentencias de expresión (como la asignación ), sentencias de retorno , las expresiones de control de las sentencias , , , o - y cada una de las tres expresiones en una sentencia.a=b;ifswitchwhiledowhilefor
  4. Antes de que se ejecute una función en una llamada a función, no se especifica el orden en que se evalúan los argumentos, pero este punto de secuencia significa que todos sus efectos secundarios se completan antes de que se ejecute la función. En la expresión , se llama con un parámetro del valor original de , pero se incrementa antes de entrar en el cuerpo de . De manera similar, y se actualizan antes de entrar en y respectivamente. Sin embargo, no se especifica en qué orden se ejecutan , , ni en qué orden se incrementan , , . Si el cuerpo de accede a las variables y , podría encontrar que ambas, ninguna o solo una de ellas se han incrementado. (La llamada a función no utiliza el operador coma; el orden de evaluación de , , y no está especificado).f(i++)+g(j++)+h(k++)fiifjkghf()g()h()ijkfjkf(a,b,c)abc
  5. Al retornar una función, después de que el valor de retorno se copia en el contexto de llamada. (Este punto de secuencia solo se especifica en el estándar de C++; en C solo está presente implícitamente. [ 4 ] )
  6. Al final de un inicializador ; por ejemplo, después de la evaluación de 5en la declaración .inta=5;
  7. Entre cada declarador en cada secuencia de declaradores; por ejemplo, entre las dos evaluaciones de en . [ 5 ] (Este no es un ejemplo del operador coma.)a++intx=a++,y=a++
  8. Después de cada conversión asociada con un especificador de formato de entrada/salida. Por ejemplo, en la expresión , hay un punto de secuencia después de que se evalúa y antes de imprimir .printf("foo %n %d",&a,42)%n42

C11 y C++11

C++11 introdujo diversos cambios, incluyendo características que mejoran el rendimiento, la usabilidad y la multihilo del lenguaje. [ 6 ] En parte debido a la introducción del soporte del lenguaje para hilos, C11 y C++11 introdujeron una nueva terminología para el orden de evaluación. Una operación puede ser "secuenciada antes" que otra, pueden ser "secuenciadas indeterminadamente" (una debe completarse antes que la otra) o pueden ser "no secuenciadas" (las operaciones en cada expresión pueden estar intercaladas).

C++17

C++17 restringió varios aspectos del orden de evaluación. La newexpresión siempre realizará la asignación de memoria antes de evaluar los argumentos del constructor. Los operadores <<, >>, ., .*, ->*, y el operador de subíndice y el operador de llamada a función tienen garantizada su evaluació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());

Ahora se garantiza que se llamará a a, b, y cen ese orden. El lado derecho de cualquier operador de asignación se evalúa antes que el lado izquierdo, por lo que b() *= a();se garantiza que se evaluará aprimero. Finalmente, aunque el orden en que se evalúan los parámetros de la función sigue dependiendo de la implementación, el compilador ya no puede intercalar subexpresiones entre varios parámetros. [ 7 ]

Véase también

Referencias

  1. Cláusula 6.5#2 de la especificación C99 : "Entre el punto de secuencia anterior y el siguiente, el valor almacenado de un objeto se modificará como máximo una vez mediante la evaluación de una expresión. Además, solo se accederá al valor anterior para determinar el valor que se almacenará."
  2. El Anexo C de la especificación C99 enumera las circunstancias en las que se puede asumir un punto de secuencia.
  3. El estándar C++ de 1998 enumera los puntos de secuencia para ese lenguaje en la sección 1.9, párrafos 16 a 18.
  4. Estándar C++, ISO 14882:2003, sección 1.9, nota al pie 11.
  5. Estándar C++, ISO 14882:2003, sección 8.3: "Cada declarador init en una declaración se analiza por separado como si estuviera en una declaración por sí mismo."
  6. Abdulwahab, Saddam; Rashwan, Hatem; Cristiano, Julian; Chambon, Sylvie; Puig, Domenec (2019). "Registro 2D/3D efectivo mediante características de prominencia curvilínea y SVM multiclase". Actas de la 14.ª Conferencia Internacional Conjunta sobre Visión por Computadora, Procesamiento de Imágenes y Teoría y Aplicaciones de Gráficos por Computadora . SCITEPRESS - Publicaciones de Ciencia y Tecnología. pp. 354–361 . doi : 10.5220/0007362603540361 . ISBN  978-989-758-354-4.
  7. Dos Reis, Gabriel; Sutter, Herb; Caves, Jonathan (23 de junio de 2016). "Refinamiento del orden de evaluación de expresiones para C++ idiomático" (PDF) . open-std.org . págs. 1–5 . Consultado el 28 de abril de 2023 . 
  • Pregunta 3.8 de las preguntas frecuentes sobre comp.lang.c