【问题标题】:Why it is valid to intertwine switch/for/if statements in C/C++? [duplicate]为什么在 C/C++ 中交织 switch/for/if 语句是有效的? [复制]
【发布时间】:2020-01-06 08:35:25
【问题描述】:

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

的展开形式
reenter (this) {
  yield ..
  yield ..
}

似乎是交织在一起的 switch/if/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;
}

【问题讨论】:

标签: c++ c language-lawyer


【解决方案1】:

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

所以你的代码

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 标签,因此它们可以放置在任何地方。

【讨论】:

    【解决方案2】:

    从句法上讲,switch 的主体只是一个语句(通常,但不一定是复合语句)

    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-labels 和常规标签视为相同,只有语义检查验证case/default-labels 在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 标签也与常规标签一样,只是它们也用于(最常见的)计算跳转。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-11
      • 1970-01-01
      • 2012-07-13
      相关资源
      最近更新 更多