Почему допустимо переплетать операторы switch / for / if в C / C ++?

Я читаю 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;
}

Это некая уловка устройства Даффа.   —  person PeopleMoutainPeopleSea    schedule 06.01.2020

Если вы используете boost, значит, вы пишете C ++, а не C. Итак, вы должны спросить, почему это действительный C ++, а не почему он действителен C (даже если ответы, вероятно, будут очень похожими).   —  person PeopleMoutainPeopleSea    schedule 06.01.2020

Хотите добавить часть исходного кода?   —  person PeopleMoutainPeopleSea    schedule 06.01.2020

оператор switch с чередованием do-while, Как работает устройство Даффа?   —  person PeopleMoutainPeopleSea    schedule 06.01.2020

Это не имеет ничего общего с boost-asio. Это вопрос синтаксиса. Не ставьте теги без разбора.   —  person PeopleMoutainPeopleSea    schedule 06.01.2020

См. также:  Препроцессор GCC не работает? Время компиляции больших файлов с комментариями или без них
Понравилась статья? Поделиться с друзьями:
IT Шеф
Комментарии: 2
  1. PeopleMoutainPeopleSea

    Синтаксически тело переключателя — это просто оператор (обычно, но не обязательно составной оператор).

    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();
    }
    

    Синтаксис обрабатывает _5 _ / _ 6_-меток и обычные метки одинаково, только семантическая проверка проверяет, что _7 _ / _ 8_-меток находятся внутри switch.

    С точки зрения реализации все компилируется в (плоскую) сборку.

    E.g.

    if(test) YesBranch; else ElseBranch;
    

    сплющен в (псевдосборка)

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

    и нет причин, по которым что-либо в таком плоском коде нельзя было пометить.

    _12 _ / _ 13_ метки также похожи на обычные метки, за исключением того, что они также используются (чаще всего) в вычисляемом прыжке.

  2. PeopleMoutainPeopleSea

    Причина в том, что операторы переключения не являются структурированными операторами потока управления. Вместо этого их следует рассматривать как синтаксический сахар для статической отправки. 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, поэтому их можно размещать где угодно.

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: