Definición de variable dentro de la statement del interruptor

En el siguiente código, ¿por qué a la variable i no se le asigna el valor 1 ?

 #include  int main(void) { int val = 0; switch (val) { int i = 1; //i is defined here case 0: printf("value: %d\n", i); break; default: printf("value: %d\n", i); break; } return 0; } 

Cuando compilo, recibo una advertencia de que no i inicializaron a pesar de int i = 1; eso lo inicializa claramente

 $ gcc -Wall test.c warning: 'i' is used uninitialized in this function [-Wuninitialized] printf("value %d\n", i); ^ 

Si val = 0 , entonces la salida es 0 .

Si val = 1 o cualquier otra cosa, entonces la salida también es 0.

Por favor, explíqueme por qué la variable i se declara pero no se define dentro del interruptor. El objeto cuyo identificador es i existe con la duración de almacenamiento automático (dentro del bloque) pero nunca se inicializa. ¿Por qué?

De acuerdo con el estándar C (6.8 Declaraciones y bloques), énfasis mío:

3 Un bloque permite que un conjunto de declaraciones y declaraciones se agrupen en una unidad sintáctica. Los inicializadores de objetos que tienen duración de almacenamiento automática y los declaradores de matriz de longitud variable de identificadores ordinarios con ámbito de bloque se evalúan y los valores se almacenan en los objetos (incluido el almacenamiento de un valor indeterminado en objetos sin inicializador) cada vez que se declara alcanzado en el orden de ejecución, como si fuera una statement, y dentro de cada statement en el orden en que aparecen los declaradores.

Y (6.8.4.2 La statement de cambio)

4 Una instrucción switch hace que el control salte hacia , dentro o más allá de la instrucción que es el cuerpo del interruptor, dependiendo del valor de una expresión controladora, y de la presencia de una etiqueta predeterminada y los valores de las tags de casos en o en el cambiar de cuerpo. Solo se puede acceder a una etiqueta de caso o predeterminada dentro de la statement de interruptor de cierre más cercana.

Por lo tanto, el inicializador de la variable i nunca se evalúa porque la statement

  switch (val) { int i = 1; //i is defined here //... 

no se alcanza en el orden de ejecución debido a los saltos a las tags de las cajas y al igual que cualquier variable con la duración de almacenamiento automático tiene un valor indeterminado.

Ver también este ejemplo normativo de 6.8.4.2/7:

EJEMPLO en el fragmento de progtwig artificial

 switch (expr) { int i = 4; f(i); case 0: i = 17; /* falls through into default code */ default: printf("%d\n", i); } 

el objeto cuyo identificador es i existe con la duración de almacenamiento automático (dentro del bloque) pero nunca se inicializa, y por lo tanto, si la expresión de control tiene un valor distinto de cero, la llamada a la función printf tendrá acceso a un valor indeterminado. Del mismo modo, la llamada a la función f no puede ser alcanzada.

En el caso de que val no sea cero, la ejecución salta directamente al valor predeterminado de la etiqueta. Esto significa que la variable i , mientras está definida en el bloque, no se inicializa y su valor es indeterminado.

6.8.2.4 La statement de cambio

  1. Una instrucción switch hace que el control salte hacia, dentro o más allá de la instrucción que es el cuerpo del interruptor, dependiendo del valor de una expresión de control, y de la presencia de una etiqueta predeterminada y los valores de las tags de cada caso en el interruptor cuerpo. Solo se puede acceder a una etiqueta de caso o predeterminada dentro de la statement de interruptor de cierre más cercana.

De hecho, tu i está declarado dentro del bloque de switch , por lo que solo existe dentro del switch . Sin embargo, su inicialización nunca se alcanza, por lo que permanece sin inicializar cuando val no es 0.

Es un poco como el siguiente código:

 { int i; if (val==0) goto zerovalued; else goto nonzerovalued; i=1; // statement never reached zerovalued: i = 10; printf("value:%d\n",i); goto next; nonzerovalued: printf("value:%d\n",i); goto next; next: return 0; } 

Intuitivamente, piense en una statement sin procesar como preguntarle al comstackdor por alguna ubicación (en el marco de llamadas en su stack de llamadas, o en un registro, o lo que sea), y piense en la inicialización como una statement de asignación. Ambos son pasos separados, y se puede ver una statement de inicialización en C como int i=1; como azúcar sintáctico para la statement cruda int i; seguido por la asignación de inicialización i=1; .

(en realidad, las cosas son un poco más complejas, por ejemplo, con int i= i!=i; y aún más complejas en C ++)

Línea para la inicialización de i variable int i = 1; nunca se llama porque no pertenece a ninguno de los casos disponibles.

La inicialización de variables con duraciones de almacenamiento automático se detalla en C11 6.2.4p6 :

  1. Para un objeto que no tiene un tipo de matriz de longitud variable, su duración se extiende desde la entrada en el bloque con el que está asociado hasta que la ejecución de ese bloque termina de alguna manera. (Ingresar un bloque cerrado o llamar a una función suspende, pero no termina, la ejecución del bloque actual.) Si el bloque se ingresa recursivamente, se crea una nueva instancia del objeto cada vez. El valor inicial del objeto es indeterminado. Si se especifica una inicialización para el objeto, se realiza cada vez que se alcanza la statement o el literal compuesto en la ejecución del bloque; de lo contrario, el valor se vuelve indeterminado cada vez que se alcanza la statement.

Es decir, la vida de i en

 switch(a) { int i = 2; case 1: printf("%d",i); break; default: printf("Hello\n"); } 

es de { a } . Su valor es indeterminado , a menos que la statement int i = 2; se alcanza en la ejecución del bloque . Dado que la statement está antes de cualquier etiqueta de caso, la statement no se puede alcanzar nunca, ya que el switch salta a la etiqueta de la caja correspondiente, y durante la inicialización.

Por lo tanto, i sin inicializar. Y como lo hace, y dado que su dirección nunca se tomó, el uso del valor no inicializado para un comportamiento indefinido C11 6.3.2.1p2 :

  1. […] Si lvalue designa un objeto de duración de almacenamiento automático que podría haber sido declarado con la clase de almacenamiento de registros (nunca se tomó su dirección), y ese objeto no está inicializado (no declarado con un inicializador y sin asignación a él) realizado antes del uso), el comportamiento no está definido.

(Tenga en cuenta que el estándar en sí mismo aquí indica incorrectamente los contenidos en el paréntesis aclaratorio: se declara con un inicializador pero el inicializador no se ejecuta).