¿Qué son los puntos de secuencia en C++ y qué tienen que ver con el comportamiento indefinido?

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

El arma secreta del depurador: lo que los programadores de C++ pasan por alto

\n\n

¿Alguna vez has escrito algo como i = i++ + ++i; y obtuviste resultados diferentes en distintos compiladores? ¿O atrapaste un error extraño que solo se reproducía en el servidor, pero no en tu portátil? Conoce a: undefined behavior (comportamiento indefinido) — uno de los enemigos más traicioneros del desarrollador de C++. Y en el centro de este caos están los puntos de secuencia (sequence points).

\n\n

En este artículo analizaremos qué son los sequence points, cómo se relacionan con el orden de evaluación en C++, y —lo más importante— cómo no pisar este rastrillo. El material se basa en una pregunta icónica de Stack Overflow que ha acumulado miles de visitas. ¡Allá vamos!

\n\n

¿Qué son los puntos de secuencia (sequence points)?

\n\n

Imagina que eres el director de una orquesta. Cada músico (instrucción del procesador) debe entrar en un momento estrictamente definido. Un punto de secuencia es un momento en el tiempo en el que todos los «efectos secundarios» anteriores (cambios en variables, llamadas a funciones) ya están garantizados como completados, y los siguientes aún no han comenzado.

\n\n

En el estándar C++ 98/03 se usaba el término sequence point. En C++11 se reemplazó por las relaciones más estrictas «sequenced before / sequenced after», pero la esencia sigue siendo la misma: entre dos puntos de secuencia, el compilador tiene todo el derecho de reordenar las operaciones, siempre que no cambie el resultado (según la regla «as-if»).

\n\n

En pocas palabras: si intentas modificar la misma variable dos veces entre dos puntos de secuencia — obtienes undefined behavior. El compilador puede hacer cualquier cosa: desde un resultado correcto hasta formatear tu disco duro (es broma, pero casi es cierto).

\n\n

Ejemplos clásicos de puntos de secuencia

\n
    \n
  • El punto y coma ; — final de una expresión
  • \n
  • La coma , como operador (¡pero no en los argumentos de una función!)
  • \n
  • El final de cada expresión en un for (después de la inicialización, la condición y el paso)
  • \n
  • Los operadores lógicos && y ||
  • \n
  • El operador ternario ?:
  • \n
  • La llamada a una función (antes de que el cuerpo de la función comience a ejecutarse)
  • \n
\n\n

¿Cómo generan los puntos de secuencia un comportamiento indefinido?

\n\n

Aquí tienes un ejemplo clásico que todo desarrollador de C++ debería conocer:

\n\n
int i = 0;\ni = i++ + ++i;  // ¡UB! Comportamiento indefinido
\n\n

¿Por qué es UB? Porque entre dos puntos de secuencia (en este caso, entre el inicio y el final de la expresión) la variable i se modifica dos veces:

\n
    \n
  1. El incremento postfijo i++
  2. \n
  3. El incremento prefijo ++i
  4. \n
  5. La asignación i = ...
  6. \n
\n\n

El compilador no sabe en qué orden ejecutar estas operaciones. ¿El resultado? Un compilador dará 2, otro 3, un tercero 0. Y en modo de optimización podría incluso eliminar este código por completo.

\n\n

Otro caso engañoso: argumentos de función

\n\n
int foo(int a, int b) { return a + b; }\n\nint x = 5;\nint result = foo(++x, ++x);  // ¡UB!
\n\n

Aquí la coma entre los argumentos no es un operador coma, sino un separador. ¡No hay un punto de secuencia entre la evaluación de los argumentos ++x! Ambos incrementos se ejecutan «en paralelo» (desde el punto de vista del estándar), y nuevamente obtenemos undefined behavior.

\n\n

Ejemplos prácticos: cómo se ve el UB en código real

\n\n

Ejemplo 1: Error no obvio con indexación

\n\n
int arr[5] = {0, 10, 20, 30, 40};\nint idx = 1;\narr[idx] = idx++;  // ¡UB!
\n\n

A primera vista todo es simple: queremos escribir 1 en arr[1] y luego incrementar idx. Pero el estándar no garantiza el orden de evaluación del índice idx y la parte derecha idx++. ¿El resultado? Podría escribirse en arr[1], o podría ser en arr[2].

\n\n

Ejemplo 2: Comp

Blogs

Book Recommendations