675 votos

Comportamiento indefinido y puntos de secuencia

¿Qué son los "Puntos de Secuencia"? ¿Cuál es la relación entre un Comportamiento Indefinido y Secuencia de Puntos?

Yo uso a menudo divertidas y complicadas expresiones como a[++i] = i;, para hacerme sentir mejor. ¿Por qué debo dejar de usarlos?

Si usted ha leído esto, asegúrese de visitar la pregunta de un Comportamiento Indefinido y la Secuencia de los Puntos de Recarga.

(Nota: Este está destinado a ser una entrada de Stack Overflow en C++ de preguntas frecuentes. Si usted quiere a la crítica de la idea de ofrecer un documento de preguntas frecuentes en este formulario, a continuación de la publicación en el meta, la que comenzó todo este sería el lugar para hacerlo. Las respuestas a esa pregunta son monitoreados en el C++ sala de chat, donde las preguntas frecuentes idea comenzó en el primer lugar, entonces, la respuesta es muy probable que se lea por los que vinieron con la idea).

474voto

Prasoon Saurav Puntos 47488

Descargo De Responsabilidad : Esta Bien. Esta respuesta es un poco larga. Así que tengan paciencia mientras lo lee. Si usted ya sabe estas cosas, la lectura de ellos de nuevo, no hacer una locura.

Pre-requisitos : conocimientos básicos de C++ Estándar


¿Cuáles son los Puntos de Secuencia?

La Norma dice que

En ciertos puntos especificados en la ejecución de la secuencia de llamada secuencia de puntos, todos los efectos secundarios de las anteriores evaluaciones deberá ser completa y sin efectos secundarios de las siguientes evaluaciones se han llevado a cabo. (§1.9/7)

Efectos secundarios? ¿Cuáles son los efectos secundarios?

La evaluación de una expresión produce algo y si además hay un cambio en el estado del entorno de ejecución se dice que la expresión (la evaluación) tiene algún efecto secundario(s).

Por ejemplo:

int x = y++; //where y is also an int

Además de la operación de inicialización el valor de y se cambió debido a los efectos secundarios de las ++ operador.

Tan lejos tan bueno. De pasar a la secuencia de puntos. Una alternancia definición de seq-puntos dados por el comp.lang.c autor Steve Summit:

Secuencia de punto es un punto en el tiempo en el que el polvo se ha asentado y todos los efectos secundarios que se han observado hasta el momento se garantiza que sea completa.


¿Cuáles son los comunes de la secuencia de los puntos mencionados en el Estándar de C++?

Estos son:

  • al final de la evaluación de la expresión total (§1.9/16) (Un total de expresión es una expresión que no es una subexpresión de otra expresión.)1

Ejemplo :

int a = 5; // ; is a sequence point here
  • en la evaluación de cada una de las siguientes expresiones : después de la evaluación de la primera expresión(§1.9/18) 2

    • a && b (§5.14)
    • a || b (§5.15)
    • a ? b : c (§5.16)
    • a , b (§5.18) ( func(a,a++) , no es un operador coma (,) es simplemente un separador entre los argumentos a y a++. El comportamiento no está definido en ese caso, si a se considera un tipo primitivo)
  • en una llamada a una función (o no, si la función está en línea), tras la evaluación de todos los argumentos de la función (si las hubiera) que se lleva a cabo antes de la ejecución de las expresiones o manifestaciones en el cuerpo de la función (§1.9/17).

1 : Nota : la evaluación de una expresión puede incluir la evaluación de las subexpresiones que no son léxicamente parte de la expresión. Por ejemplo, las subexpresiones que participan en la evaluación de argumento por defecto (expresiones 8.3.6) son considerados a ser creado en la expresión que llama a la función, no la expresión que define el argumento por defecto

2 : Los operadores indicados son los operadores integrados, como se describe en la cláusula 5. Cuando uno de estos operadores sobrecargados (cláusula 13) en una válida contexto, por lo tanto la designación de un definidos por el usuario operador de la función, la expresión designa a una invocación de función y los operandos forma de una lista de argumentos, sin implícito una secuencia entre ellos.


¿Qué es un Comportamiento Indefinido?

El Estándar define un Comportamiento Indefinido en la Sección §1.3.12 como

el comportamiento, como las que pueden surgir en la utilización de una errónea programa de construcción o datos erróneos, por lo que esta Norma Internacional impone ningún requisito 3.

Un comportamiento indefinido puede esperar cuando se esta La Norma internacional, se omite la descripción de cualquier definición explícita de comportamiento.

3 : permitido un comportamiento indefinido rangos de ignorar la situación por completo con resultados impredecibles, a comportarse durante la traducción o la ejecución del programa en un documentado característica forma de medio ambiente (con o con- la emisión de un mensaje de diagnóstico), para terminar una traducción o de ejecución (con la emisión de un mensaje de diagnóstico).

En definitiva, un comportamiento indefinido significa que cualquier cosa puede ocurrir a partir de demonios que vuelan fuera de su nariz a su novia para quedar embarazada.


¿Cuál es la relación entre un Comportamiento Indefinido y Secuencia de Puntos?

Antes de entrar en lo que usted debe saber la diferencia(s) entre un Comportamiento Indefinido, sin especificar el Comportamiento y Aplicación Definidos de Comportamiento.

Usted también debe saber que the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.

Por ejemplo:

int x = 5, y = 6;

int z = x++ + y++; //it is unspecified whether x++ or y++ will be evaluated first.

Otro ejemplo aquí.


Ahora el Estándar en §5/4 dice

  • 1) Entre el anterior y el siguiente punto de la secuencia de un escalar objeto tendrá su valor almacenado modificado en más de una vez por la evaluación de una expresión.

¿Qué significa esto?

De manera informal que significa que entre dos puntos de secuencia de una variable no puede ser modificada más de una vez. En una expresión de la declaración, la next sequence point está usualmente en el punto y coma, y el previous sequence point está en el final de la anterior declaración. Una expresión puede también contener intermedio sequence points.

De la oración anterior las siguientes expresiones invocar un Comportamiento Indefinido.

i++ * ++i; // i is modified more than once
i = ++i    // same as above
++i = 2;   // same as above
i = ++i +1 // same as above
++++++i;   //parsed as (++(++(++i)))

i = (i,++i,++i); // Undefined Behaviour because there's no sequence point between `++i`(right most) and assignment to `i` (`i` gets modified more than once b/w two SP)

Pero las expresiones siguientes son bellas

i = (i, ++i, 1) + 1; //well defined (AFAIK)
i = (++i,i++,i) // well defined 
int j = i;
j = (++i, i++, j*i); // well defined

  • 2) por otra parte, el valor previo serán accesibles únicamente para determinar el valor que se almacenará.

¿Qué significa esto? Esto significa que si un objeto se escribe dentro de una expresión, de cualquiera y de todos los accesos a la misma dentro de la misma expresión debe estar directamente involucrados en el cálculo del valor a ser escrito.

Por ejemplo, en i = i +1 todo el acceso de i (en L.H.S y en R.H.S) están directamente involucrados en el cálculo del valor a ser escrito. Entonces está bien.

Esta regla efectivamente restringe legal expresiones para aquellos en los que los accesos demostrable preceder a la modificación.

Ejemplo 1:

 std::printf("%d %d", i,++i); // invokes Undefined Behaviour because of Rule no 2

Ejemplo 2:

 a[i] = i++ // or a[++i] = i or a[i++] = ++i etc

no está permitida debido a que uno de los accesos de i (uno en a[i]) no tiene nada que ver con el valor que termina siendo almacenada en me (lo que ocurre en i++), y por lo que no hay buena manera de definir, ya sea para nuestra comprensión o el compilador--si el acceso debe realizarse antes o después de que el nuevo valor se almacena. Por lo que el comportamiento es indefinido.

Ejemplo 3 :

 int x = i + i++ ;// Similar to above

Seguimiento de la respuesta aquí.

193voto

Prasoon Saurav Puntos 47488

Este es un seguimiento a mi respuesta anterior y contiene C++0x materias relacionadas..


Pre-requisitos : conocimientos básicos de las Relaciones (Matemáticas).


Escuché a alguien decir que no hay ninguna Secuencia de Puntos en C++0x, ¿es esto cierto ?

¡Sí! Esto es muy cierto.

La secuencia de los Puntos han sido sustituidos por el más claro Secuenciado Antes Y Secuenciados Después de las relaciones en C++0x.


Pero, ¿por qué? Me encantó la secuencia de puntos. :(

La ISO C++ miembros del Comité de pensamiento que la Secuencia de los Puntos de materias eran muy difíciles de entender. Así que decidieron reemplazarlo con las mencionadas relaciones sólo para obtener más clara redacción y mejorada precisión.

Si te encantó la Secuencia de Puntos que el amor Secuenciado[Antes/Después de la] relación aún más. Así que no hay nada de que preocuparse.


¿Qué es exactamente este " Secuenciado antes de la` cosa por CIERTO?

Como se dijo antes Secuenciado Antes de la(§1.9/13) es una relación que es :

entre las evaluaciones ejecutada por un solo hilo e induce un estricto orden parcial1


Formalmente significa dio ninguna de las dos evaluaciones(Ver a continuación)A y B, si A está secuenciado antes B, luego de la ejecución de A deberá preceder a la ejecución de B. Si A no está secuenciado antes B y B no está secuenciado antes A, a continuación, A y B son unsequenced 2.

Evaluaciones A y B son indeterminately secuenciado cuando cualquiera A es la secuencia de antes B o B es la secuencia de antes A, pero no se especifica que3.

[NOTAS]
1 : Un estricto orden parcial es una relación binaria "<" a través de un conjunto P cual es asymmetricy transitive, es decir, para todos los a, by c en P, tenemos que:
........(i). si a < b, entonces (b < a) (asymmetry);
........(ii). si a < b y b < c entonces a < c (transitivity).
2 : La ejecución de unsequenced evaluaciones pueden superponerse.
3 : Indeterminately secuenciado las evaluaciones no se superponen, pero bien podría ser ejecutado en primer lugar.


¿Cuál es el significado de la palabra " evaluación " en el contexto de C++0x ?

En C++0x evaluación de una expresión (o una sub-expresión) en general incluye:

  • valor de cálculos (incluyendo la determinación de la identidad de un objeto para glvalue evaluación y obtención de un valor previamente asignado a un objeto para prvalue evaluación) y

  • la iniciación de los efectos secundarios.

Ahora (§1.9/14) dice :

Cada valor de la computación y los efectos secundarios asociados con un completo expresión es secuenciado antes de cada valor de la computación y los efectos secundarios asociados con la siguiente expresión para ser evaluado.

  • Ejemplo(trivial) :

    int x; x = 10; ++x;
    Valor de cálculo y efectos secundarios asociados con ++x es la secuencia después de que el valor de la computación y los efectos secundarios de x = 10;


Así que debe haber alguna relación entre un Comportamiento Indefinido y el mencionado cosas, ¿verdad?

¡Sí! Derecho.

En (§1.9/15) se ha mencionado que

Excepto donde se indique lo contrario, las evaluaciones de los operandos de los operadores individuales y de las subexpresiones de las expresiones individuales son unsequenced4.

Por ejemplo :

int main()
{
     int num = 19 ;
     num = (num << 3) + (num >> 3) ;
} 

1) Evaluación de los operandos de + operador unsequenced respecto a otros.
2) la Evaluación de los operandos de << y >> de los operadores están unsequenced respecto a otros.

4: En una expresión que se evalúa más de una vez durante la ejecución de un programa, unsequenced y indeterminately secuenciado las evaluaciones de sus subexpresiones no tiene necesidad de realizarse de forma coherente en los diferentes evaluaciones

(§1.9/15) El valor de los cálculos de los operandos de una el operador se ordenan antes de que el valor de cálculo del resultado del operador.

Que significa en x + y el valor de cálculo de x y y son secuenciados, antes de que el valor de cálculo de (x + y).

Lo que es más importante

(§1.9/15) Si un efecto secundario de un escalar objeto es unsequenced relativa a

(a) otro efecto secundario en el mismo escalar objetos

o

(b) un valor de cálculo utilizando el valor de la misma escalar objeto.

el comportamiento es indefinido.

Ejemplos :

int i =5, v[10] = { };
void  f(int,  int);

1) i = i++ * ++i ; //Undefined Behaviour
2) i = ++i + i++ ; //Undefined Behaviour 
3) i = v[i++];     //Undefined Behaviour
4) i = i++ + 1 ;   //Undefined Behaviour 
5) i = ++i + 1 ; //well defined behaviour
6) ++++i ; //well defined behaviour

7) f(i  =  -1,  i  =  -1); // Undefined Behaviour (see below)

Cuando se llama a una función (o no, si la función está en línea), cada valor de la computación y los efectos secundarios asociados con cualquier argumento expresión, o con el postfix expresión que designa a la llamada de la función, es la secuencia de antes de la ejecución de cada expresión o instrucción en el cuerpo de la función llamada. [ Nota: el Valor de los cálculos y los efectos secundarios asociados con argumentos diferentes expresiones unsequenced. - fin de la nota ]

Expresiones (5) y (6) no invocar un comportamiento indefinido. Revise las siguientes respuestas para una explicación más detallada.


Nota Final :

Si usted encontrar cualquier error en el post, por favor deja un comentario. El poder de los usuarios (Con rep >20000) por favor no dudes en editar el post para corregir errores tipográficos y otros errores.

74voto

La secuencia de puntos

La secuencia de puntos de puntos en la ejecución de un programa donde todos los efectos secundarios producidos por la evaluación antes de la secuencia de puntos han sido completados. Efectos secundarios producidos por las evaluaciones que se producen después de la secuencia de punto inolvidable para ser separados de los efectos secundarios producidos por las evaluaciones que se producen antes de que la secuencia y punto de suceder después.

Evaluaciones

Evaluar significa algo para aplicar algunas de tiempo de ejecución de la semántica de una expresión. Hay sin evaluar expresiones (operandos de sizeof, algunos operandos de typeid y tal) inspeccionar solamente la expresión del tipo y no tienen significado en tiempo de ejecución. Si se evalúa una expresión, puede resultar en un valor que puede implicar la lectura de los valores de los objetos, o que sólo puede evaluar a un objeto sin leer el valor de la misma (que, a continuación, sigue siendo un lvalue, como con la izquierda subexpresión de una asignación). Además, puede producir efectos secundarios como sea necesario. Una evaluación es completa si su valor es conocido, pero, hasta que una secuencia de punto ha sido alcanzado, los efectos secundarios producidos por la evaluación se asume que están siendo procesados.

Usted tiene la secuencia de puntos después de todas las evaluaciones que usualmente son necesarios para ser procesado en su totalidad antes de que algo de cierto hay otras expresiones que se procesan. Estas son

  • Después de la evaluación de a en a && b y a || b y a ? b : c. También después de la evaluación de a en a, b - este operador se llama el "operador coma".
  • Para una llamada a una función, después de evaluar los argumentos de llamada de función y antes de iniciar las evaluaciones en el cuerpo de la función.
  • Después de la evaluación de una expresión completa (que no fue evaluado como parte de otra expresión). Ejemplos son las condiciones del bucle, si las condiciones, cambie los valores y la expresión de las declaraciones.
  • Inmediatamente antes de una función termina (por desenrollar la función de una excepción o por ordinariamente devolverlo después de que (posiblemente) la creación, el valor de retorno). Esto se asegura de que cada efecto secundario en una función realmente ha sido resuelto y es completamente transformados.

Efectos secundarios

Un efecto secundario es un cambio en el entorno de ejecución del programa que sucede además de calcular un valor. Esto puede ser (entre otros) escrito a un objeto, llamar a una entrada/salida de la función o llamando a una función que lo hace.

El flujo de ejecución del programa

Con estos tres términos, el flujo de un programa se puede visualizar de la siguiente manera. En los siguientes diagramas, E(X) especifica la evaluación de un (sub-)la expresión x, % especifica una secuencia de punto y un S(k, e) especifica un efecto secundario k sobre un objeto e. Si una evaluación de las necesidades para leer un valor de un objeto con nombre (si x es un nombre), la evaluación está escrito como V(x), de lo contrario es escrito como E(x). Los efectos secundarios se escriben de derecha y de izquierda, a las expresiones. Una arista entre dos expresiones, significa que la parte superior de la expresión se evalúa antes que la menor expresión (generalmente debido a la menor expresión depende del valor o lvalue de la parte superior de la expresión).

Si miras a las dos de la expresión declaraciones i++; i++;, puede representar el diagrama siguiente

E(i++) -> { S(increment, i) }
   |
   %
   |
E(i++) -> { S(increment, i) }
   |
   %

Como se puede observar, hay dos puntos de secuencia, y uno de ellos se separa de las dos modificaciones de i. Llamada a la función de los argumentos también son interesantes, aunque voy a omitir el diagrama para este

int c = 0;
int d = 0;
void f(int a, int b) { assert((a == c - 1) && (b == d - 1)); }
int main() { f(c++, d++); }

La aserción está bien, porque es seguro que cuando f's cuerpo es ejecutado, los efectos secundarios producidos por el argumento de las evaluaciones completa: para Ello, c y d han sido completamente incrementa.

Consideremos la expresión de instrucción i++ * j++;

{ S(increment, i) } <- E(i++)      E(j++) -> { S(increment, j) }
                           \       /
                            +--+--+
                               |
                         E(i++ * j++)
                               |
                               %

Wow, en donde las dos ramas? Recuerde que a partir de la definición inicial de la secuencia de punto: la Secuencia de los puntos de afectar a las evaluaciones que se producen antes . Todas las subexpresiones de la multiplicación son evaluados previo a ti y no hay otra secuencia de punto, de modo que debemos asumir "la máxima parallelity" para encontrar donde potencialmente tenemos concurriendo escribe para el mismo objeto. Más formalmente, las dos ramas no están ordenados. La secuencia de punto de relación es una relación que las órdenes de algunas evaluaciones para cada uno de los otros y no el fin de los demás: Es por ello un orden parcial.

Conflicto de efectos secundarios

Para dar el compilador de la máxima libertad en la generación y optimización de código máquina, los casos como el de la multiplicación de arriba no de la secuencia de las evaluaciones de las subexpresiones y no separe los efectos secundarios producidos por los mismos, excepto en los pocos casos descritos anteriormente. Esto puede llevar a conflictos, y el Estándar de C++ marcas comportamiento de los programas indefinido si se intenta modificar el mismo objeto sin una secuencia de intervención del punto (en realidad, se aplica a escalar objetos, debido a que otros objetos son no modificables (arrays) o simplemente no son aplicables a esta regla (objetos de la clase)). El comportamiento también es indefinido si el anterior valor es de lectura desde el objeto, pero no es una modificación demasiado, como en i * i++

// This yields to undefined behavior!
// Left 'i' is not guaranteed to read new value:

    V(i)        E(i++) -> { S(increment, i) })
      \         /
       +---+---+
           |
       E(i * i++)
           |
           %

Como excepción, se permite leer el valor del objeto, si es necesario para calcular el nuevo valor. Este es el caso en i = i + 1

                V(i)        E(1)
                   \         /
                    +---+---+
                        |
  E(i)              E(i + 1)
     \                 /
      +-------+-------+
              |
        E(i = i + 1) -> { S(assign, i) }
              |
              %

Como vemos aquí, el valor de i se lee en el lado derecho y después de la evaluación de ambos lados de la asignación se lleva a cabo. Así que tenemos un efecto secundario y la lectura de i'valor de s sin una secuencia de intervención, pero la lectura era sólo para determinar que el valor se almacena en i, entonces está bien.

A veces, un valor que se lee después de una modificación que se hizo. Este es el caso de a = (b = 0), que en C++ se escriben a b y a continuación se lee desde b, sin una secuencia de intervención. Sin embargo, esto está bien, porque no lee el anterior valor de b, pero el nuevo valor de la misma. En este caso, el efecto secundario de la asignación a b ha sido completa, no sólo antes de la siguiente secuencia de punto, pero también antes de la lectura de b, según sea necesario para la cesión a para obtener el nuevo valor de b. En la especificación, esta relación se establece por explícita restricciones, en este caso concierne, en particular, b = 0 y lee "El resultado de la operación de asignación es el valor que se almacena en el operando de la izquierda después de que la cesión ha tenido lugar; el resultado es un lvalue." ¿Por qué no una secuencia de punto para hacer esta relación? Porque una secuencia de punto tendría el efecto indeseable de que requiere que cada efecto secundario que ocurre en la evaluación de la izquierda y la derecha operando para ser completo, en lugar de hacerlo sólo para la asignación en el caso de que su resultante lvalue es leer.

Palabras de cierre

Cabe señalar que los empleos temporales creados en la evaluación de una expresión que normalmente no se limpian antes de la siguiente secuencia de punto, pero sólo cuando la expresión ha sido completamente evaluado (en ciertas situaciones, la vida de empleos temporales en lugar de ser incluso mayor, si se hace referencia obligado para ellos).

10voto

Yttrill Puntos 2461

Supongo que hay una razón fundamental para el cambio, no es meramente cosmética para hacer que el viejo interpretación más clara: la razón de que es la simultaneidad. No especificado orden de elaboración es simplemente la selección de uno de los varios posibles serie de órdenes, esto es muy diferente antes y después de la compra, porque si no se especifica el pedido, simultáneas evaluación es posible: no es así con las viejas reglas. Por ejemplo en:

f (a,b)

anteriormente ya sea una, a continuación, b, o, b, a continuación, una. Ahora, a y b pueden ser evaluadas con instrucciones intercalado o incluso en diferentes núcleos.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X