【问题标题】:Variable definition inside switch statementswitch 语句中的变量定义
【发布时间】:2021-08-11 10:51:01
【问题描述】:

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

【问题讨论】:

  • 我在问case标签变量定义之前的朋友。因为我正在尝试使用 i 是 switch 内部的局部变量。
  • 请不要作为 C++ 副本关闭。查找 C 版本。
  • 这不是 @user3121023 的帖子的副本,因为在链接的问题中,i 的声明是 inside case 声明,所以你只需换行它在括号周围。在这种情况下,i 的声明在任何 case 语句之外,我不确定这是有效的 C。
  • 我不知道这个问题的答案,所以不得不用谷歌搜索。有解释here
  • @undur_gongor 我修复了那个,显然我是错误地将其关闭为骗子的人之一。它现在重新打开,因为它没有重复。那里的答案是马马虎虎,所以最好关闭那个作为骗子:)

标签: c initialization switch-statement declaration


【解决方案1】:

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

6.8.2.4 switch 语句

  1. switch 语句会导致控制跳转到、进入或越过作为 开关体,取决于控制表达式的值,以及是否存在 默认标签和开关体上或开关体中的任何大小写标签的值。一个案例或 默认标签只能在最近的封闭 switch 语句中访问。

【讨论】:

    【解决方案2】:

    确实,您的iswitch 块内声明,因此它只存在于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】:

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

      【讨论】:

      • 初始化不需要为了发生而被“调用”。想想全局变量。
      【解决方案4】:

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

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

      And(6.8.4.2 switch 语句)

      4 switch 语句使控件跳转到、进入或越过 作为 switch 主体的语句,取决于 a 的值 控制表达式,并在存在默认标签和 开关体上或开关体中的任何大小写标签的值。案例或违约 标签只能在最近的封闭开关内访问 声明。

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

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

      由于跳转到案例标签而未达到执行顺序,并且与任何具有自动存储持续时间的变量一样具有不确定的值。

      另请参阅 6.8.4.2/7 中的这个规范示例:

      EXAMPLE 在仿真程序片段中

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

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

      【讨论】:

      • 很好的答案,我认为 6.8 中的部分有必要解释这一点。您能否将我在答案中引用的示例也添加到您的答案中? (因为这是一个规范的例子。请随意复制/粘贴我的答案中的格式内容)然后我将删除我的答案以支持你的答案。
      • 我认为,关键部分是“好像它是一个声明”。它应该更加突出。
      • 事实上,任何不提及自动存储时长的答案都是不完整的。因为如果您将i 声明为static,然后瞧,代码突然按预期运行。这意味着这与 switch 语句的行为没有太大关系,而是与具有自动存储持续时间的变量的初始化规则有关。
      【解决方案5】:

      自动存储时长的变量初始化详见C11 6.2.4p6

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

      i

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

      {}。它的值是indeterminate除非在执行块时达到声明int i = 2;。由于声明在任何 case 标签之前,因此永远无法到达声明,因为 switch 会跳转到相应的 case 标签 - 并超过初始化。

      因此i 保持未初始化状态。既然这样做了,而且它的地址从未被占用,那么将未初始化的值用于未定义的行为 C11 6.3.2.1p2:

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

      (请注意,此处的标准本身对澄清括号中的内容的表述不正确 - 它使用初始化程序声明但未执行初始化程序)。

      【讨论】:

        猜你喜欢
        • 2020-06-29
        • 1970-01-01
        • 2013-12-31
        • 2011-12-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多