英文:
Why it is valid to intertwine switch/for/if statements in C/C++?
问题
I'm reading boost/asio/coroutine.hpp
and cannot understand the implementation of BOOST_ASIO_CORO_REENTER and BOOST_ASIO_CORO_YIELD. The expanded form of
reenter (this) {
yield ..
yield ..
}
seems to be intertwined switch/if/for statements. I'm wondering how come this is valid C code? I wrote something similar (shown below) and found it compiles using gcc.
int main() {
int a = 1;
switch (a)
case 0: if (1) a = 2;
else case 1: for (;;) {
case 3:
break;
}
return 0;
}
英文:
I'm reading boost/asio/coroutine.hpp
and cannot understand the implementation of BOOST_ASIO_CORO_REENTER and BOOST_ASIO_CORO_YIELD. The expanded form of
reenter (this) {
yield ..
yield ..
}
seems to be intertwined switch/if/for statements. I'm wondering how come this is valid C code? I wrote something similar (shown below) and found it compiles using gcc.
int main() {
int a = 1;
switch (a)
case 0: if (1) a = 2;
else case 1: for (;;) {
case 3:
break;
}
return 0;
}
答案1
得分: 3
以下是翻译好的部分:
原因是因为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标签,因此它们可以放在任何地方。
英文:
The reason is because switch statements aren't structured control flow statements. Instead, they should be considered as syntactic sugar for static dispatch. Dispatch means that the control flow is redirected and static means that the compiler knows where it is redirected to.
So your code
int a = 1;
switch (a)
case 0: if (1) a = 2;
else case 1: for (;;) {
case 3:
break;
}
return 0;
would be compiled into something roughly equivalent of
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;
where dispatch
is a function the compiler runs for emitting the machine code required for the static dispatch. Since all dispatch values are constants and all dispatch targets are known by the compiler, it can generate very efficient machine code.
As for why it is legal, I guess the reason is because there's no particular reason for it to be illegal. As shown, case
statements are just goto labels so they can be placed wherever.
答案2
得分: 2
从语法上讲,switch
语句的主体只是一个语句(通常是一个复合语句,但不一定是)6.8:
语句:
标记语句
复合语句
表达式语句
选择语句
迭代语句
跳转语句
这些语句可以被标记6.8.1:
标记语句:
标识符 : 语句
case 常量表达式 : 语句
default : 语句
示例:
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
标签是否在 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
标签与常规标签几乎相同,只是它们通常也用于计算跳转。
英文:
Syntactically, the body of a switch is just a statement (usually, but not necessarily a compound statement)
6.8:
statement:
labeled-statement
compound-statement
expression-statement
selection-statement
iteration-statement
jump-statement
which may be labeled
6.8.1:
labeled-statement:
identifier : statement
case constant-expression : statement
default : statement
Example:
switch(1) one: case 1: dothis();
If it is a compound statement, then each substatement recursively may also be labeled. Example:
switch(x) {
if(1) one: case 1: dothis();
else case 0: orthis(); /*fallthru*/
three: case 3: three();
}
The syntax treats case
/default
-labels and regular labels the same, only the semantic check verifies that case
/default
-labels be inside a switch
.
Implementation-wise, everything compiles into (flat) assembly.
E.g.
if(test) YesBranch; else ElseBranch;
is flattened into (pseudo-assembly)
IF_NOT_THEN_GOTO(test, PAST_YES_BRANCH)
YesBranch
goto PAST_NO_BRANCH;
NoBranch
PAST_NO_BRANCH:;
and there's no reason why anything in such flat code couldn't be labeled.
case
/default
labels are also just like regular labels except they're also used in (most usually) a computed jump.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论