secundarios - vacunas
¿Llamar una función con efectos secundarios locales dos veces en la misma expresión es un comportamiento indefinido? (5)
Aquí hay una cita de C11 Sección 6.5 Párrafo 2:
Si un efecto secundario en un objeto escalar no tiene secuencia en relación con un efecto secundario diferente en el mismo objeto escalar o un cálculo de valor utilizando el valor del mismo objeto escalar, el comportamiento no está definido. Si hay varios ordenamientos permitidos de las subexpresiones de una expresión, el comportamiento no está definido si se produce un efecto secundario no secuencial en cualquiera de los ordenamientos.84
Pero en la Sección 5.1.2.3, Párrafo 3, el ordenamiento está secuenciado de manera indeterminada, no sin secuencia, por lo que la primera oración anterior no se aplica. La segunda oración solo se aplica si hay efectos secundarios sin secuencia en cualquiera de los ordenamientos. Pero en cada uno de los ordenamientos no hay efectos secundarios sin secuencia.
El estándar C11 parece guardar silencio sobre el efecto de los cálculos secuencialmente indeterminados. Quizás debamos concluir que es aceptable que un compilador conforme produzca salidas de cualquiera de las posibles secuencias. En esta interpretación hay 2 secuencias posibles, las cuales dan como resultado que g () devuelva el valor 3.
Así que creo que esto es aceptable.
Tenga en cuenta que en C99 no hay ninguna sección correspondiente a la Sección 5.1.2.3 en C11. La sección 5.5, párrafo 3, dice que el orden de las subexpresiones y el orden en que se producen los efectos secundarios no están especificados.
"Sin especificar" no es lo mismo que "sin definir".
Esto me lleva a creer que en C99 este comportamiento no es indefinido.
int f() {
static int i=0;
return ++i;
}
int g() {
return f() + f();
}
¿ g()
devuelve 3
o el resultado es undefined
?
Las evaluaciones de los dos operandos del operador + no tienen secuencia 1 .
Hay un punto de secuencia justo antes de la llamada a la función real 2 . Este punto de secuencia es suficiente para separar las modificaciones de la variable estática i, haciendo que toda la expresión sea secuenciada de forma indeterminada, y el orden de la función se llama sin especificar 3 .
Por lo tanto, el comportamiento permanece definido y la primera llamada a la función g siempre producirá 3, ya que el orden no especificado de las llamadas a la función no influye en el resultado.
Se define un programa que contiene un comportamiento no especificado 4 .
(Todas las citas son de: ISO / IEC 9899: 201x)
1 (6.5 Expresiones 3)
Excepto como se especifica más adelante, los efectos secundarios y los cálculos de valor de las subexpresiones no son posteriores.
2 (6.5.2.2 Llamadas de función 10)
Hay un punto de secuencia después de las evaluaciones del designador de la función y los argumentos reales, pero antes de la llamada real.
3 (5.1.2.3 Ejecución del programa 3)
Las evaluaciones A y B se secuencian de forma indeterminada cuando A se secuencia antes o después de B, pero no se especifica cuál.
4 (4. Conformidad 3)
Un programa que sea correcto en todos los demás aspectos, que funcione con datos correctos, que contenga un comportamiento no especificado, será un programa correcto y actuará de acuerdo con 5.1.2.3.
No hay razón para que esto no esté definido, porque la operación +
es conmutativa, y porque hay puntos de secuencia para las dos operaciones ++
a secuenciar.
El estándar C tiene puntos de secuencia después de una expresión completa, y también antes de ingresar una función en una llamada de función. Por lo tanto, los resultados de ++
serán completamente secuenciados. Además, como +
es conmutativo, el orden de las llamadas a f()
no cambia el resultado.
Tenga en cuenta que la misma lógica no se aplicaría a
return f() - f();
porque -
no es conmutativo. El resultado de la expresión anterior no está especificado , es decir, un compilador compatible con el estándar podría producir razonablemente un 1
o un -1
, según el orden en que el compilador llame a las dos funciones f()
.
No hay razón para un comportamiento indefinido. La variable estática se almacenará en el espacio de memoria .BSS y no se realizará ninguna copia; el compilador se encargará de esto. La función f()
se llamará dos veces, secuencialmente. El siguiente caso es similar:
for (int i = 0; i < 2; ++i)
g += f();
Si la función f()
hubiera sido llamada como dos hilos, habría resultado en un comportamiento indefinido.
6.5.2.2 Llamadas de función
...
10 Hay un punto de secuencia después de las evaluaciones del designador de la función y los argumentos reales, pero antes de la llamada real. Cada evaluación en la función de llamada (incluidas otras llamadas de función) que no está secuenciada específicamente antes o después de la ejecución del cuerpo de la función llamada está secuenciada de forma indeterminada con respecto a la ejecución de la función llamada. 94) 94) En otras palabras, las ejecuciones de funciones no se "intercalan" entre sí
El resultado final es que hay un punto de secuencia entre cada ++i
en virtud de que forma parte de una llamada de función. Por lo tanto, este comportamiento está bien definido.
Si realmente hace lo que pretendes es otro asunto. Tenga en cuenta que en algún momento se arriesga a un desbordamiento firmado, que no está definido. Y como otros han señalado, f() - f()
puede no dar el resultado que esperaría (la evaluación de izquierda a derecha no está garantizada en ese caso).