由于一个小错别字,我偶然发现了这个结构:

int main(void) {
    char foo = 'c';

    switch(foo)
    {
        printf("Cant Touch This\n");   // This line is Unreachable

        case 'a': printf("A\n"); break;
        case 'b': printf("B\n"); break;
        case 'c': printf("C\n"); break;
        case 'd': printf("D\n"); break;
    }

    return 0;
}

看起来switch语句顶部的printf是有效的,但也完全不可访问。

我得到了一个干净的编译,甚至没有关于不可达代码的警告,但这似乎毫无意义。

编译器应该将此标记为不可访问的代码吗? 这有什么用吗?


当前回答

你得到了与所需的gcc选项-Wswitch-unreachable生成警告相关的答案,这个答案是关于可用性/价值部分的详细说明。

直接引用C11第6.8.4.2章,(我的重点)

开关(expr) { Int I = 4; f(我); 例0: I = 17; /*变为默认代码*/ 默认值: printf (" % d \ n”,我); } 标识符为I的对象存在自动存储 持续时间(在块内),但从未初始化,因此如果 控制表达式具有非零值,即对printf的调用 函数将访问一个不确定的值。类似地,调用 函数f不能得到。

这是不言自明的。您可以使用它来定义一个局部作用域变量,该变量仅在switch语句作用域内可用。

其他回答

需要注意的是,switch语句中的代码实际上没有结构限制,也没有大小写*:标签放在代码*中的位置。这使得像duff的设备这样的编程技巧成为可能,一个可能的实现看起来像这样:

int n = ...;
int iterations = n/8;
switch(n%8) {
    while(iterations--) {
        sum += *ptr++;
        case 7: sum += *ptr++;
        case 6: sum += *ptr++;
        case 5: sum += *ptr++;
        case 4: sum += *ptr++;
        case 3: sum += *ptr++;
        case 2: sum += *ptr++;
        case 1: sum += *ptr++;
        case 0: ;
    }
}

你看,switch(n%8){和case 7:标签之间的代码绝对是可达的…


正如supercat在评论中指出的那样:自C99以来,goto和label(无论是否为case *: label)都不能出现在包含VLA声明的声明范围内。因此,说格*:标签的放置没有结构上的限制是不正确的。然而,达夫的设备早于C99标准,而且它并不依赖于VLA标准。然而,由于这个原因,我不得不在我的第一句话中插入一个“virtually”。

不仅用于变量声明,还用于高级跳转。你可以很好地利用它,当且仅当你不倾向于面条代码。

int main()
{
    int i = 1;
    switch(i)
    {
        nocase:
        printf("no case\n");

        case 0: printf("0\n"); break;
        case 1: printf("1\n"); goto nocase;
    }
    return 0;
}

打印

1
no case
0 /* Notice how "0" prints even though i = 1 */

需要注意的是,开关箱是最快的控制流条款之一。所以它对程序员来说必须非常灵活,有时会涉及到这样的情况。

有一个著名的用法叫做达夫的方法。

int n = (count+3)/4;
switch (count % 4) {
  do {
    case 0: *to = *from++;
    case 3: *to = *from++;
    case 2: *to = *from++;
    case 1: *to = *from++;
  } while (--n > 0);
}

这里我们将from指向的缓冲区复制到to指向的缓冲区。我们复制count个数据实例。

do{}while()语句开始于第一个case标签之前,并且case标签嵌入在do{}while()中。

这将do{}while()循环结束时遇到的条件分支数量减少了大约4倍(在本例中;常量可以被调整为你想要的任何值)。

现在,优化器有时可以为您做到这一点(特别是当他们正在优化流/向量化指令时),但如果没有配置文件引导的优化,他们无法知道您是否期望循环很大。

一般来说,变量声明可以出现在那里,并且在任何情况下都可以使用,但在切换结束后将超出范围。(注意任何初始化都将被跳过)

此外,不是特定于开关的控制流可以进入开关块的部分,如上面所示,或者使用goto。

你得到了与所需的gcc选项-Wswitch-unreachable生成警告相关的答案,这个答案是关于可用性/价值部分的详细说明。

直接引用C11第6.8.4.2章,(我的重点)

开关(expr) { Int I = 4; f(我); 例0: I = 17; /*变为默认代码*/ 默认值: printf (" % d \ n”,我); } 标识符为I的对象存在自动存储 持续时间(在块内),但从未初始化,因此如果 控制表达式具有非零值,即对printf的调用 函数将访问一个不确定的值。类似地,调用 函数f不能得到。

这是不言自明的。您可以使用它来定义一个局部作用域变量,该变量仅在switch语句作用域内可用。

可以用它实现“循环半”,尽管这可能不是最好的方式:

char password[100];
switch(0) do
{
  printf("Invalid password, try again.\n");
default:
  read_password(password, sizeof(password));
} while (!is_valid_password(password));