2016-03-08 63 views
13

在以下代码中,为什么变量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的对象存在自动存储持续时间(在块内),但不会初始化。为什么?

+0

我在询问有关标签变量定义的朋友之前。因为我想使用我是交换机内部的局部变量。 – sakthi

+3

请不要以C++重复的方式关闭。找到一个C版本。 – 2501

+2

这不是@ user3121023发布的帖子的重复,因为在链接问题中,'i'的声明在** case内部是**,所以您只需将它包装在括号中即可。在这种情况下,'i'的声明在任何'case'声明之外,我不确定这是否有效C. – Holt

回答

4

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

6.8.2.4 switch语句

  • switch语句使控制跳转到,进入或过去认为是 开关本体,这取决于该语句控制表达式的值以及默认标签的存在和交换机主体上或交换机主体上的任何案例标签的值。一个案例或 默认标签只能在最近的封闭开关语句中访问。
  • 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++更复杂的)

    1

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

    +2

    初始化不需要“调用”即可实现。考虑全局变量。 –

    12

    根据C标准(6。8声明和块),重点是我的:

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

    和(6.8.4.2 switch语句)

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

    因此可变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); 
    } 
    

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

    +1

    不错的答案,我认为6.8的部分是必要的来解释这一点。你能不能把我在答案中引用的例子添加到你的答案中? (既然它是一个规范的例子,请随意从我的答案中复制/粘贴格式化的东西)然后,我将删除我的答案,以支持你的答案。 – Lundin

    +0

    我认为,关键部分是“好像它是一个声明”。它应该更加突出。 –

    +1

    事实上,任何没有提及自动存储时间的答案都是不完整的。因为如果你将'i'声明为'static',那么瞧,代码的行为就像预期的那样。这意味着这与switch语句的行为没有太大关系,就像初始化具有自动存储持续时间的变量的规则一样。 – Lundin

    1

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

  • 对于这样一个对象,该对象不具有可变长度数组类型,它的寿命从入口延伸到与其相关的块,直到该块的执行以任何方式结束。 (输入一个封闭的程序段或调用一个函数会暂停但不会结束当前程序段的执行。)如果程序段递归输入,则每次创建一个新的对象实例。对象的初始值是不确定的。如果为对象指定了初始化,则每次执行块时都会执行声明或复合字面值时执行初始化;否则,每次达到声明时该值都变得不确定。
  • 即,的i

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

    寿命是从{}。它的值是不确定,,除非在执行块时达到声明int i = 2;。由于声明位于任何案例标签之前,因此switch将跳转到相应的案例标签 - 以及初始化,因此无法达到声明。

    因此i保持未初始化。而且,由于它,并且由于它具有其地址从来没有采取,利用未初始化的值,以未定义行为C11 6.3.2.1p2

  • [...]如果左值指定一个可以用寄存器存储类声明的自动存储持续时间的对象(从未取得其地址),并且该对象是未初始化的(未使用初始化器声明,并且在使用之前未对其进行赋值),则行为未定义。
  • (请注意,该标准本身在这里词语的澄清括号不正确的内容 - 它声明了一个初始化,但不执行初始化)。