Definición variable dentro de la declaración del interruptor
initialization switch-statement (5)
De acuerdo con el estándar C (6.8 Declaraciones y bloques), el énfasis es mío:
3 Un bloque permite agrupar un conjunto de declaraciones y declaraciones en una unidad sintáctica. Los inicializadores de objetos que tienen una duración de almacenamiento automática y los declaradores de matriz de longitud variable de identificadores ordinarios con alcance de bloque, se evalúan y los valores se almacenan en los objetos (incluido el almacenamiento de un valor indeterminado en objetos sin un inicializador) cada vez que se realiza la declaración alcanzado en el orden de ejecución, como si fuera una declaración, y dentro de cada declaración en el orden en que aparecen los declaradores.
Y (6.8.4.2 La declaración 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 switch, dependiendo del valor de una expresión controladora y de la presencia de una etiqueta predeterminada y los valores de cualquier etiqueta de caso en o en el cambiar de cuerpo. Solo se puede acceder a una etiqueta de caso o predeterminada dentro de la declaración de interruptor adjunta más cercana.
Por lo tanto, el inicializador de la variable
i
nunca se evalúa porque la declaración
switch (val) {
int i = 1; //i is defined here
//...
no se alcanza en el orden de ejecución debido a saltos a etiquetas de caso y, como cualquier variable con la duración de almacenamiento automático, tiene un valor indeterminado.
Vea también este ejemplo normativo de 6.8.4.2/7:
EJEMPLO En el fragmento de programa 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 una duración de almacenamiento automática (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 accederá a un valor indeterminado. Del mismo modo, no se puede acceder a la llamada a la función f.
En el siguiente código, ¿por qué a la variable no se le asigna el valor
1
?
#include <stdio.h>
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 sobre que no se está inicializando a pesar de
int i = 1;
que claramente lo inicializa
$ 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, la salida también es 0.
Explíqueme por qué la variable
i
se declara pero no se define dentro del conmutador.
El objeto cuyo identificador es
i
existe con una duración de almacenamiento automática (dentro del bloque) pero nunca se inicializa.
¿Por qué?
De hecho, su
i
se
declara
dentro del bloque del
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 la declaración en bruto como pedirle al compilador alguna ubicación (en el marco de la llamada en su pila de llamadas, o en un registro, o lo que sea), y piense en la inicialización como una declaración de asignación.
Ambos son pasos separados, y podría mirar una declaración de inicialización en C como
int i=1;
como azúcar sintáctico para la declaración cruda
int i;
seguido de 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;
e incluso más complejo en C ++)
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
, aunque se define en el bloque, no se inicializa y su valor es indeterminado.
6.8.2.4 La declaración de cambio
- Una instrucción de cambio hace que el control salte hacia, dentro o más allá de la declaració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 cualquier etiqueta de caso en o en el interruptor cuerpo. Solo se puede acceder a una etiqueta de caso o predeterminada dentro de la declaración de interruptor adjunta más cercana.
Línea para la inicialización de la variable i 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 :
- Para un objeto de este tipo que no tiene un tipo de matriz de longitud variable, su vida útil 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. (Al ingresar un bloque cerrado o llamar a una función se suspende, pero no finaliza, la ejecución del bloque actual.) Si el bloque se ingresa de forma recursiva, 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 declaración o el literal compuesto en la ejecución del bloque; de lo contrario, el valor se vuelve indeterminado cada vez que se alcanza la declaración.
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
declaración
int i = 2;
se alcanza en la ejecución del bloque
.
Dado que la declaración está antes de cualquier etiqueta de caso, no se puede alcanzar la declaración, ya que el
switch
salta a la etiqueta de caso correspondiente y sobre la inicialización.
Por lo tanto, permanezco sin inicializar. Y como lo hace, y dado que nunca toma su dirección, el uso del valor no inicializado para un comportamiento indefinido C11 6.3.2.1p2 :
- [...] Si lvalue designa un objeto de duración de almacenamiento automático que podría haberse declarado con la clase de almacenamiento de registro (nunca se tomó su dirección), y ese objeto no se ha inicializado (no se ha declarado con un inicializador y no se le ha asignado ninguna asignación) realizado antes del uso), el comportamiento es indefinido.
(Observe que el estándar en sí mismo redacta incorrectamente los contenidos en el paréntesis de aclaración: se declara con un inicializador pero el inicializador no se ejecuta).