为什么在 C/C++ 中交织 switch/for/if 语句是有效的?

Why it is valid to intertwine switch/for/if statements in C/C++?

本文关键字:语句 if 有效 for C++ 为什么 switch      更新时间:2023-10-16

我正在阅读boost/asio/coroutine.hpp,无法理解BOOST_ASIO_CORO_REENTER和BOOST_ASIO_CORO_YIELD的实现。扩展形式

reenter (this) {
yield ..
yield ..
}

似乎是交织在一起的开关/如果/for 语句。我想知道为什么这是有效的 C 代码? 我写了类似的东西(如下所示(,发现它是使用 gcc 编译的。

int main() {
int a = 1;
switch (a)
case 0: if (1) a = 2;
else case 1: for (;;) {
case 3:
break;
}
return 0;
}

原因是 switch 语句不是结构化控制流语句。相反,它们应被视为静态调度的句法糖。调度意味着控制流被重定向,静态意味着编译器知道它被重定向到哪里。

所以你的代码

int a = 1;
switch (a)
case 0: if (1) a = 2;
else case 1: for (;;) {
case 3:
break;
}
return 0;

将被编译成大致等效的东西

int a = 1;
void *dest = dispatch(a, { case0_addr, case1_addr, case3_addr });
goto *dest;
case0_addr:
if (1) { 
a = 2;
} else {
case1_addr:
for (;;) {
case3_addr:
goto case_end;
}
}
case_end:
return 0;

其中dispatch是编译器运行的函数,用于发出静态调度所需的机器代码。由于所有调度值都是常量,并且编译器知道所有调度目标,因此它可以生成非常有效的机器代码。

至于为什么它是合法的,我想原因是因为它没有特别的理由是非法的。如图所示,case语句只是 goto 标签,因此它们可以放置在任何位置。

语法上,开关的主体只是一个语句(通常,但不一定是复合语句(

6.8:

statement:
labeled-statement
compound-statement
expression-statement
selection-statement
iteration-statement
jump-statement

可以标记 6.8.1:

labeled-statement:
identifier : statement
case constant-expression : statement
default : statement

例:

switch(1) one: case 1: dothis();

如果是复合语句,那么每个子语句也可以递归地标记。例:

switch(x) {
if(1) one: case 1: dothis();
else case 0: orthis();  /*fallthru*/
three: case 3: three();
}

语法将case/default标签和常规标签视为相同,只有语义检查验证case/default-label是否在switch内。

在实现方面,一切都编译成(平面(程序集。

例如

if(test) YesBranch; else ElseBranch;

被展平成(伪程序集(

IF_NOT_THEN_GOTO(test, PAST_YES_BRANCH)
YesBranch
goto PAST_NO_BRANCH;
NoBranch
PAST_NO_BRANCH:;

而且没有理由不能标记这种平面代码中的任何内容。

case/default标签也与常规标签一样,只是它们也用于(最常见的(计算跳转。