switch语句中的变量定义

时间:2016-03-08 11:30:55

标签: c initialization switch-statement declaration

在以下代码中,为什么变量i未分配值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;
}

当我编译时,我收到一条警告,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的对象存在自动存储持续时间(在块内)但从未初始化。为什么呢?

5 个答案:

答案 0 :(得分:12)

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

  

3块允许对一组声明和语句进行分组   成一个句法单位。 具有的对象的初始值设定项   自动存储持续时间,以及可变长度数组声明符   对具有块范围的普通标识符进行评估和值   存储在对象中(包括存储不确定的值   在没有初始化程序的对象中)每次声明时   按执行顺序到达,就好像它是一个声明,和   在声明者出现的顺序中的每个声明中。

和(6.8.4.2开关声明)

  

4切换语句导致控件跳转到,进入或超过   声明是开关体,取决于a的值   控制表达式,以及是否存在默认标签和   开关体上或开关体内的任何案例标签的值。案例或默认情况   标签只能在最近的封闭开关内访问   言。

因此,永远不会评估变量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的调用。

答案 1 :(得分:4)

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

  

6.8.2.4开关语句

     
      
  1. switch语句使控制跳转到,进入或超过语句   切换主体,取决于控制表达式的值,以及是否存在   默认标签以及开关主体上或中的任何案例标签的值。一个案例或   默认标签只能在最近的封闭开关语句中访问。
  2.   

答案 2 :(得分:3)

确实,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;的语法糖,然后是初始化作业i=1;

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

答案 3 :(得分:1)

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

答案 4 :(得分:1)

具有自动存储持续时间的变量的初始化详见C11 6.2.4p6

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

即。

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

{}。除非在块的执行中达到声明int i = 2; ,否则其值为 indeterminate 。由于声明在任何案例标签之前,因此switch跳转到相应的案例标签 - 以及初始化时,无法达到声明。

因此i仍未初始化。因为它确实存在,并且因为它的地址从未被采用过,所以将未初始化的值用于未定义的行为 C11 6.3.2.1p2

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

(请注意,标准本身在此澄清了澄清括号中的内容 - 它是使用初始化程序声明的,但初始化程序未执行)。