c arrays runtime-error designated-initializer unspecified-behavior

Valores extraños al inicializar la matriz utilizando los inicializadores designados



arrays runtime-error (5)

Cuando inicializo la matriz debajo de todo, la salida se ve bien, excepto por los values[3] . Por alguna razón, los values[3] inicializados como values[0]+values[5] están generando un número muy grande. Supongo que estoy tratando de asignar values[0]+values[5] antes de que se almacenen correctamente en la memoria, pero si alguien pudiera explicar eso sería genial.

int main (void) { int values[10] = { [0]=197,[2]=-100,[5]=350, [3]=values[0] + values[5], [9]= values[5]/10 }; int index; for (index=0; index<10; index++) printf("values[%i] = %i/n", index, values[index]); return 0; }

La salida es la siguiente:

values[0] = 197 values[1] = 0 values[2] = -100 values[3] = -1217411959 values[4] = 0 values[5] = 350 values[6] = 0 values[7] = 0 values[8] = 0 values[9] = 35


Esta es la primera vez que veo algo inicializado de esa manera, pero me di cuenta de que el comportamiento que está viendo tenía que ver con acceder a una parte de la matriz que aún no se ha inicializado. Así que lo construí utilizando GCC 4.6.3 en un sistema Ubuntu 12.04 de 32 bits. En mi entorno, obtuve resultados diferentes a los tuyos.

gcc file.c -o file ./file values[0] = 197 values[1] = 0 values[2] = -100 values[3] = 197 values[4] = 0 values[5] = 350 values[6] = 0 values[7] = 0 values[8] = 0 values[9] = 35 objdump -d file > file.asm cat file.asm (relevant portion posted below) 080483e4 <main>: 80483e4: 55 push %ebp 80483e5: 89 e5 mov %esp,%ebp 80483e7: 57 push %edi 80483e8: 53 push %ebx 80483e9: 83 e4 f0 and $0xfffffff0,%esp 80483ec: 83 ec 40 sub $0x40,%esp 80483ef: 8d 5c 24 14 lea 0x14(%esp),%ebx 80483f3: b8 00 00 00 00 mov $0x0,%eax 80483f8: ba 0a 00 00 00 mov $0xa,%edx 80483fd: 89 df mov %ebx,%edi 80483ff: 89 d1 mov %edx,%ecx 8048401: f3 ab rep stos %eax,%es:(%edi) <===== 8048403: c7 44 24 14 c5 00 00 movl $0xc5,0x14(%esp) 804840a: 00 804840b: c7 44 24 1c 9c ff ff movl $0xffffff9c,0x1c(%esp) 8048412: ff 8048413: 8b 54 24 14 mov 0x14(%esp),%edx 8048417: 8b 44 24 28 mov 0x28(%esp),%eax 804841b: 01 d0 add %edx,%eax 804841d: 89 44 24 20 mov %eax,0x20(%esp) 8048421: c7 44 24 28 5e 01 00 movl $0x15e,0x28(%esp) 8048428: 00 8048429: 8b 4c 24 28 mov 0x28(%esp),%ecx 804842d: ba 67 66 66 66 mov $0x66666667,%edx 8048432: 89 c8 mov %ecx,%eax 8048434: f7 ea imul %edx 8048436: c1 fa 02 sar $0x2,%edx 8048439: 89 c8 mov %ecx,%eax 804843b: c1 f8 1f sar $0x1f,%eax

He identificado una línea clave en la salida anterior que creo que marca la diferencia entre lo que generó el suyo y lo que generó el mío (marcado con <======). Antes de que los elementos específicos de la matriz se inicialicen con los valores que especificó, el mío es poner a cero el contenido de la matriz. La inicialización específica de los elementos de la matriz se produce después de esto.

Dado el comportamiento anterior, no creo que no sea razonable suponer que el tuyo no puso a cero el contenido de la matriz antes de inicializar elementos específicos de la matriz. En cuanto a por qué la diferencia en el comportamiento? Solo puedo especular; pero mi primera suposición es que estamos usando dos versiones diferentes del compilador.

Espero que esto ayude.


Esto no tiene nada que ver con los inicializadores designados como tales. Es el mismo error que obtendrías al intentar algo como esto:

int array[10] = {5, array[0]};

El orden en que se ejecutan las expresiones de la lista de inicialización es simplemente un comportamiento no especificado. Lo que significa que es específico del compilador, no documentado y nunca se debe confiar en:

C11 6.7.9 / 23

Las evaluaciones de las expresiones de la lista de inicialización se secuencian de manera indeterminada entre sí y, por lo tanto, no se especifica el orden en el que aparecen los efectos secundarios.

Dado que está utilizando elementos de matriz para inicializar otros miembros de la matriz, esto significa que debe cambiar su código a la asignación en tiempo de ejecución en lugar de la inicialización.

int values[10]; values[2] = -100; values[5] = 350; values[3] = values[0] + values[5]; ...

Como efecto secundario, su programa ahora también será mucho más legible.


Parece que está sujeto a un comportamiento no especificado aquí, ya que el orden de evaluación de las expresiones de la lista de inicialización no está especificado, según el borrador de la sección 6.7.8 norma C99:

El orden en que aparecen los efectos secundarios entre las expresiones de la lista de inicialización no está especificado. 133)

y nota 133 dice:

En particular, el orden de evaluación no necesita ser el mismo que el orden de inicialización del subobjeto.

Por lo que puedo decir, el texto normativo que respalda la nota 133 sería de la sección 6.5 :

Salvo que se especifique más adelante, el [...] orden de evaluación de las subexpresiones y el orden en que se producen los efectos secundarios no están especificados.

y podemos ver que un intializer es una expresión completa de 6.8 ( énfasis mío ):

Una expresión completa es una expresión que no forma parte de otra expresión o de un declarador. Cada uno de los siguientes es una expresión completa: un inicializador ; [...]

Después de mirar hacia atrás en una de mis antiguas respuestas en C ++ que cubrían los puntos de secuencia dentro de un inicializador y que colocaba la expresión completa en un lugar diferente al que originalmente concluí, me di cuenta de que la gramática en 6.7.8 contenía el inicializador dos veces:

initializer: assignment-expression { initializer-list } { initializer-list , } initializer-list: designationopt initializer initializer-list , designationopt initializer

Originalmente no me di cuenta de esto y pensé que la declaración sobre expresiones completas se aplicaba al elemento superior en la gramática anterior.

Ahora creo que, como C ++, la expresión completa se aplica a cada inicializador dentro de la lista de inicializadores, lo que hace que mi análisis anterior sea incorrecto.

El informe de defectos 439 confirmó mi sospecha de que este era el caso, contiene el siguiente ejemplo:

#include <stdio.h> #define ONE_INIT ''0'' + i++ % 3 #define INITIALIZERS [2] = ONE_INIT, [1] = ONE_INIT, [0] = ONE_INIT int main() { int i = 0; char x[4] = { INITIALIZERS }; // case 1 puts(x); puts((char [4]){ INITIALIZERS }); // case 2 puts((char [4]){ INITIALIZERS } + i % 2); // case 3 }

y dice:

En cada uso de la macro INICIALIZADORES, la variable i se incrementa tres veces. En los casos 1 y 2, no hay un comportamiento indefinido, porque los incrementos están en expresiones que están secuenciadas de forma indeterminada entre sí, no sin secuencia.

por lo que cada intializador dentro de INITIALIZERS es una expresión completa .

Como este informe de defectos está en contra de C11, vale la pena señalar que C11 es más detallado que C99 en el texto normativo sobre este tema y dice:

Las evaluaciones de las expresiones de la lista de inicialización se secuencian de manera indeterminada entre sí y, por lo tanto, no se especifica el orden en el que aparecen los efectos secundarios. 152)

Existe un comportamiento indefinido en el caso de que las siguientes expresiones se evalúen antes de asignar los elementos respectivos en los values a:

values[0] + values[5]

o:

values[5]/10

Este es un comportamiento indefinido ya que el uso de un valor indeterminado invoca un comportamiento indefinido .

En este caso específico, la solución más simple sería realizar los cálculos a mano:

int values[10] = { [0]=197,[2]=-100,[5]=350, [3]= 197 + 350, [9]= 350/10 };

Hay otras alternativas, como hacer las asignaciones al elemento 3 y 9 después de la inicialización.


Prueba este código:

int values[10]; values[0]=197; values[2]=-100; values[5]=350; values[3]=values[0]+values[5]; values[9]=values[5]/10;

Y luego imprimes la matriz como lo has hecho.


int values[10] = { [0]=197,[2]=-100,[5]=350, [3]=values[0] + values[5], [9]= values[5]/10 };

editar:

La norma ISO C99, sección 6.7.8 (Inicialización) especifica que

La inicialización se producirá en el orden de la lista de inicializadores , cada inicializador proporcionado para un subobjeto en particular anulando cualquier inicializador previamente enlistado para el mismo subobjeto;

Pero como señaló Shafik , el orden de evaluación no tiene que coincidir con el orden de inicialización

Lo que significa que los values[0] + values[5] pueden leer valores de basura de:

  • values[0]
  • values[5] ( esto es lo que sucede en tu caso )
  • ambos
  • ninguno de ellos