2010-03-10 13 views
7

Esta es una pregunta estúpida. :)"Leer" un preincremento de POD no produce un comportamiento indefinido. ¿Por qué exactamente?

[EDIT: estúpida o no, esto resultó ser una pregunta C++ peculiaridad, ver UPDATE_2]

supongamos que tenemos:

int a = 0; // line 1 
int b = ++a; // line 2 

¿Qué ocurre en la línea 2 es (nota, los números son sólo marcadores y no se especifica el orden exacto):

     = [1: write result of (3) to result of (2)] 
        /\ 
[2: take "b" l-value] [3: convert result of (4) to an r-value ] 
         | 
         [4: take "a" l-value, "increment" and return it] 

La "escritura" en (4) está "ordenada" antes de la "lectura" en (3), y dado que no hay puntos de secuencia entre, el efecto secundario no está garantizado antes (3) (también hay una "leer" dentro de (4) sí mismo, pero ordenó antes de "escribir", por lo que no produce UB).

Entonces, ¿dónde está el error en lo de arriba?

[UPDATE, dirigido a abogados secuencia de puntos no sazonados :)]

En otras palabras, el problema es:

  1. Parece que hay una "competición" si la L- el efecto secundario de conversión de valor a valor ("lectura") o incremento ("escritura") tiene lugar primero.

  2. En C, que daría una UB, de acuerdo con JTC1/SC22/WG14 N926 "Sequence Point Analysis" * (ver, por ejemplo, Ejemplo 5: int x,y; (x=y) + x; // UB).

  3. Nota que este no sería un caso debe postincremento ser utilizado desde (3) y (4) constituirá un solo [(3): tomar "a" l-valor, convertirlo a r-valor y devolver ese valor r] con el "escribir" efecto secundario retrasado hasta algún lugar antes de que el siguiente punto de la secuencia

_

(*) Esto se parece a la lógica sistemática más limpio para el tema dado por los miembros del Comité de Normas C99.

[UPDATE_2]

  1. lección aprendida: no juzgar C++ por reglas C :)). Hice exactamente eso preguntándome por qué el N926 (que describe limpiamente el modo C99 de las cosas) "no era lo suficientemente claro" sobre el tema de los preincrementos que producen valores l.

  2. Surge la pregunta, ¿cómo se podría construir un razonamiento similar para C++, ya que no existe uno, ya que incluso en el caso C simplemente interpretar el estándar es bastante difícil, y el estándar de lenguaje C++ es mucho más complicado y oscuro.

[UPDATE_3]

Hay una discusión frente a algunos temas de interés (al menos en el ~ nuevo medio) en "The undefinedness of a common expression.", más el asunto se trate en la gente del comité here (ver " 222. Puntos de secuencia y operadores de retorno lvalue ").

+3

Hmm, interesante. El problema solo ocurre en C++, no en C (++ a en C no es un valor l). –

+1

Gracias por la actualización, lo que hace que la pregunta sea más interesante.Aparentemente tampoco fui lo suficientemente experimentado como para adivinar por tu etiqueta 'C' que solo estabas después de las consideraciones de C++. –

+0

@John Eso fue _my_ fault (y también la _inconsistency_ que conduce a la pregunta en sí, vea la actualización_2). – mlvljr

Respuesta

2

Creo que la solución puede estar en la redacción de "++ i". Dice "El valor es el nuevo valor del operando, es un lvalue". Y el comportamiento no está definido en 5/4 por "Además, solo se tendrá acceso al valor anterior para determinar el valor que se almacenará".

Por lo tanto, no estamos accediendo al anterior, sino al nuevo valor. Y entonces podemos estar bien. Sin embargo, parece ser una línea muy delgada entre el comportamiento indefinido y el comportamiento definido.

En realidad, el "valor anterior" me suena como "valor que el objeto tenía en el punto de secuencia anterior". Y si se interpreta así, entonces este constructo parece indefinido. Pero si comparamos directamente la redacción de "++ i" en 5.3/2 a 5/4, nos enfrentamos a "valor nuevo" frente a "valor anterior", y las cosas se "doblan" para un comportamiento definido ("++ i "echaría un vistazo al valor de" i "en el siguiente punto de secuencia, y produciría ese valor como el contenido del valor l resultante de" ++ i ").

+0

Entonces, ¿ves una forma de extender el método de análisis N926 para respaldar el caso de C++ en cuestión? – mlvljr

+0

Agregó un enlace de discusión comp.lang.c, si está interesado. – mlvljr

1

La frase principal en C++ 5/4 es

Entre el anterior y el siguiente punto de la secuencia de un objeto escalar tendrá su valor almacenado modificado como máximo una vez por la evaluación de una expresión.

(Y la frase citada por Johannes es el siguiente, un subordinado.) ¿Qué escalar objeto

cuando sospecha que está teniendo su valor almacenado modificado más de una vez aquí? a y b se modifican una vez cada uno en la línea 2, por lo que no hay ningún problema.

(Un texto similar se encuentra en 6.5/2 en el estándar C.)


EDIT:

La "escritura" en (4) se "ordenada" antes de la "lectura" en (3), y dado que no hay puntos de secuencia entre, el efecto secundario no está garantizado antes de (3)

Al volver a leer su pregunta, creo que la confusión proviene de una forma confusa de thinki ng aproximadamente ++a: la expresión realmente no atisba el valor futuro de a. Más bien, podría ayudar pensar en ++a como "return a+1 y como un incremento de efecto secundario a, en su tiempo libre (antes del siguiente punto de secuencia)".

Entonces, ¿a quién le importa si ese efecto secundario ocurre antes o después (3)? El valor de la expresión, que es lo que se alimenta a (3), ya está determinado.

+2

@John, el punto es que cuando se hace '++ a' en C++, * no * automáticamente cede' a + 1' como un valor independiente de cambiar su valor almacenado, porque entonces no puede ser un valor l . En C++ puedes hacer cosas como 'int * p = &++i;' que no están permitidas en C. Lo que sucede en C++ para cosas como '++ a + 1' es que modificas el valor almacenado de' a' y también lees su valor entre los mismos puntos de secuencia * no * para determinar el valor a almacenar pero para procesarlo posteriormente. –

+0

@litb: exactamente, estoy actualizando mi publicación con lo que realmente fue un comentario superado por el tuyo :)) – mlvljr

+0

Capítulo y verso? Porque sigo leyendo 5/4 y 5.3.2/2 ("El valor es el nuevo valor del operando, es un valor l") y simplemente no veo que eso implique que el objeto escalar se vuelva a leer (en un mundo como-si). –

Cuestiones relacionadas