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