switch语句中的变量定义

在下面的代码中,为什么变量i没有赋值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; } 

当我编译时,我得到一个警告, i尽管int i = 1; i仍未被初始化int i = 1; 这清楚地初始化了它

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

如果val = 0 ,则输出为0

如果val = 1或其他任何值,则输出也为0。

请向我解释为什么声明变量i但在开关内没有定义。 标识符为i的对象存在自动存储持续时间(在块内)但从未初始化。 为什么?

根据C标准(6.8声明和块),强调我的:

3块允许将一组声明和语句分组到一个语法单元中。 具有自动存储持续时间的对象的初始化程序和具有块作用域的普通标识符的可变长度数组声明符将被评估,并且 每次声明时, 值都存储在对象中 (包括在没有初始化程序的对象中存储不确定值) 按执行顺序到达,就好像它是一个语句,并在声明符出现的顺序中的每个声明中。

并且(6.8.4.2转换语句)

4 switch语句使控制跳转到 ,进入或超过作为switch主体的语句,具体取决于控制表达式的值,以及是否存在默认标签以及任何case标签的值。开关体。 只能在最近的封闭switch语句中访问case或default标签。

因此,变量i的初始化器永远不会被评估,因为声明

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

由于跳转到大小写标签而没有按执行顺序到达,并且像自动存储持续时间的任何变量具有不确定的值。

另见6.8.4.2/7中的这个规范示例:

示例在人工程序片段中

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

标识符为i的对象存在自动存储持续时间(在块内)但从未初始化,因此如果控制表达式具有非零值,则对printf函数的调用将访问不确定的值。 同样,无法访问函数f的调用。

在val不为零的情况下,执行直接跳转到标签default。 这意味着变量i虽然在块中定义,但未初始化且其值不确定。

6.8.2.4 switch语句

  1. switch语句使控制跳转到,进入或超过作为交换机主体的语句,具体取决于控制表达式的值,以及是否存在默认标签以及交换机上或中的任何案例标签的值身体。 只能在最近的封闭switch语句中访问case或default标签。

实际上,你的i声明switch块内,所以它只存在于switch 。 但是,它的初始化永远不会到达,因此当val不为0时它将保持未初始化状态。

它有点像下面的代码:

 { 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; } 

直观地,考虑原始声明,例如向编译器询问某个位置(在调用堆栈中的调用帧上,或在寄存器中,或其他任何位置),并将初始化视为赋值语句。 两者都是单独的步骤,您可以在C中查看初始化声明,如int i=1; 作为原始声明int i;语法糖int i; 然后是初始化赋值i=1;

(实际上,事情稍微复杂一些,例如int i= i!=i;在C ++中更复杂)

i变量初始化的行int i = 1; 永远不会被调用,因为它不属于任何可用的案例。

具有自动存储持续时间的变量的初始化在C11 6.2.4p6中详细说明 :

  1. 对于没有可变长度数组类型的此类对象,其生命周期从entry进入与其关联的块,直到该块的执行以任何方式结束。 (输入一个封闭的块或调用一个函数会暂停,但不会结束当前块的执行。)如果以递归方式输入该块,则每次都会创建一个新的对象实例。 对象的初始值是不确定的。 如果为对象指定了初始化,则每次在执行块时到达声明或复合文字时都会执行初始化; 否则,每次达到声明时,该值将变为不确定。

就是i的生命

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

来自{ to } 。 它的值是不确定的除非声明int i = 2; 在执行块时到达 。 由于声明在任何case标签之前,因此无法达到声明,因为switch跳转到相应的case标签 – 并且在初始化时。

因此i仍然没有初始化。 并且由于它确实如此,并且因为它的地址从未被使用过,所以未初始化的值使用未定义的行为 C11 6.3.2.1p2 :

  1. […]如果左值指定了一个自动存储持续时间的对象,该对象可以使用寄存器存储类声明(从未使用过其地址),并且该对象未初始化(未使用初始化程序声明并且没有赋值给它在使用之前执行过),行为未定义。

(请注意,此处的标准本身不正确地说明了澄清括号中的内容 – 它是使用初始化程序声明的,但不执行初始化程序)。