由于一个小错别字,我偶然发现了这个结构:
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是有效的,但也完全不可访问。
我得到了一个干净的编译,甚至没有关于不可达代码的警告,但这似乎毫无意义。
编译器应该将此标记为不可访问的代码吗?
这有什么用吗?
这有什么用吗?
是的。如果你在第一个标签之前放一个声明而不是语句,这是完全有意义的:
switch (a) {
int i;
case 0:
i = f(); g(); h(i);
break;
case 1:
i = g(); f(); h(i);
break;
}
声明和语句的规则在一般情况下对于块是共享的,所以同样的规则也允许那里的语句。
值得一提的是,如果第一个语句是一个循环结构,case标签可能会出现在循环体中:
switch (i) {
for (;;) {
f();
case 1:
g();
case 2:
if (h()) break;
}
}
如果有更可读的方式,请不要编写这样的代码,但这是完全有效的,而且f()调用是可达的。
需要注意的是,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”。
这有什么用吗?
是的。如果你在第一个标签之前放一个声明而不是语句,这是完全有意义的:
switch (a) {
int i;
case 0:
i = f(); g(); h(i);
break;
case 1:
i = g(); f(); h(i);
break;
}
声明和语句的规则在一般情况下对于块是共享的,所以同样的规则也允许那里的语句。
值得一提的是,如果第一个语句是一个循环结构,case标签可能会出现在循环体中:
switch (i) {
for (;;) {
f();
case 1:
g();
case 2:
if (h()) break;
}
}
如果有更可读的方式,请不要编写这样的代码,但这是完全有效的,而且f()调用是可达的。
有一个著名的用法叫做达夫的方法。
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。