c++ unspecified-behavior

c++ - ¿Secuencia del punto de ambigüedad, comportamiento indefinido?



unspecified-behavior (2)

Hoy me encontré con un código que muestra un comportamiento diferente en clang ++ (3.7-git), g ++ (4.9.2) y Visual Studio 2013. Después de una reducción, se me ocurrió este fragmento de código que destaca el problema:

#include <iostream> using namespace std; int len_ = -1; char *buffer(int size_) { cout << "len_: " << len_ << endl; return new char[size_]; } int main(int argc, char *argv[]) { int len = 10; buffer(len+1)[len_ = len] = ''/0''; cout << "len_: " << len_ << endl; }

g ++ (4.9.2) da esta salida:

len_: -1 len_: 10

Así que g ++ evalúa el argumento a búfer, luego el búfer (..) y luego evalúa el argumento de índice al operador de matriz. Intuitivamente esto tiene sentido para mí.

clang (3.7-git) y Visual Studio 2013 ambos dan:

len_: 10 len_: 10

Supongo que Clang y VS2013 evalúan todo lo posible antes de que se convierta en búfer (..). Esto tiene menos sentido intuitivo para mí.

Supongo que lo esencial de mi pregunta es si este es un caso claro de comportamiento indefinido.

Edit: Gracias por aclarar esto, y el comportamiento no especificado es el término que debería haber usado.


Bueno, no, no es un caso de comportamiento indefinido. Es un caso de comportamiento no especificado.

No se especifica si la expresión len_ = len se evaluará antes o después del buffer(len+1) . A partir de la salida que ha descrito, g ++ evalúa primero el buffer(len+1) y clang evalúa len_ = len .

Ambas posibilidades son correctas, ya que el orden de evaluación de esas dos sub-expresiones no está especificado. Ambas expresiones serán evaluadas (por lo tanto, el comportamiento no califica como no definido) pero el estándar no especifica el orden.


Este es un comportamiento no especificado , len_ = len está secuenciado de manera indeterminada con respecto a la ejecución del cuerpo del buffer() , lo que significa que uno se ejecutará antes que el otro, pero no se especifica qué orden, pero existe un orden, por lo que las evaluaciones no pueden traslapo por lo tanto no hay comportamiento indefinido. Esto significa que gcc , clang y Visual Studio son correctos. Por otro lado, las evaluaciones no posteriores permiten la superposición de evaluaciones que pueden conducir a un comportamiento indefinido como se indica a continuación.

Del borrador de la norma C ++ 11, sección 1.9 [intro.execution] :

[...] Todas las evaluaciones en la función de llamada (incluidas otras llamadas de función) que no están específicamente secuenciadas antes o después de la ejecución del cuerpo de la función llamada se secuencian de forma indeterminada con respecto a la ejecución de la función llamada.9 [ ...]

e indeterminadamente secuenciado se cubre un poco antes de esto y dice:

[...] Las evaluaciones A y B se secuencian de forma indeterminada cuando A se secuencia antes de B o B se secuencia antes de A, pero no se especifica cual. [Nota: las evaluaciones secuenciadas de forma indeterminada no pueden superponerse, pero cualquiera de ellas puede ejecutarse primero. "Nota final"

que es diferente a las evaluaciones sin secuencia :

[...] Si A no está secuenciada antes de B y B no está secuenciada antes de A, entonces A y B no tienen secuencia. [Nota: la ejecución de evaluaciones no secuenciales puede superponerse. "Nota final" [...]

lo que puede conducir a un comportamiento indefinido ( énfasis mío ):

Excepto donde se indique, las evaluaciones de los operandos de los operadores individuales y de las subexpresiones de las expresiones individuales no tienen secuencia . [Nota: En una expresión que se evalúa más de una vez durante la ejecución de un programa, las evaluaciones no secuenciales e indeterminadas de sus subexpresiones no deben realizarse de manera consistente en diferentes evaluaciones. —Endente final] Los cálculos de valores de los operandos de un operador se secuencian antes del cálculo de valores del resultado del operador. Si un efecto secundario en un objeto escalar no tiene secuencia en relación con otro efecto secundario en el mismo objeto escalar o un cálculo de valor utilizando el valor del mismo objeto escalar, el comportamiento es indefinido [...]

Pre C ++ 11

Pre C ++ 11, el orden de evaluación de las subexpresiones tampoco está especificado, pero utiliza puntos de secuencia en lugar de ordenar. En este caso, hay un punto de secuencia en la entrada de la función y la salida de la función que garantiza que no haya un comportamiento indefinido. De la sección 1.9 :

[...] Los puntos de secuencia en entrada de función y salida de función (como se describió anteriormente) son características de las llamadas de función evaluadas, sea cual sea la sintaxis de la expresión que llame a la función.

Clavar el orden de evaluación.

Las diferentes elecciones hechas por cada compilador pueden parecer poco intuitivas dependiendo de su perspectiva y expectativas. El tema de determinar el orden de evaluación es el tema del EWG número 158: N4228 Orden de evaluación de expresión de refinamiento para C ++ idiomático , que se está considerando para C ++ 17 pero parece controvertido en función de las reacciones a una encuesta sobre el tema . El artículo cubre un caso mucho más complicado de la 4ta edición de "The C ++ Programming Language" . Lo que demuestra que incluso aquellos con experiencia profunda en C ++ pueden ser engañados.