Случайный охват шкафа

Рассмотрение некоторых сторонних C-кода я натолкнулся на что-то вроде:

switch (state) {
case 0: 
    if (c=='A') { // open brace
        // code...
    break; // brace not closed!
case 1:
    // code...
    break;
    } // close brace!
case 2:
    // code...
    break;
}

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

Почему это действительное C?
Каков эффект на выполнение этого кода по сравнению с закрытием скобки в ожидаемом месте?
Есть ли случаи, когда это может быть полезно?

Изменить: В примере, на котором я смотрел, присутствовали все перерывы (как указано выше), но ответ также может включать в себя поведение, если разрыв отсутствует в случае 0 или 1.

Ответ 1

Не только он действителен, аналогичная структура была использована в реальном коде, например Duff Device, который представляет собой развернутый цикл для копирования буфер:

send(to, from, count)
register short *to, *from;
register count;
{
        register n = (count + 7) / 8;
        switch(count % 8) {
        case 0: do {    *to = *from++;
        case 7:         *to = *from++;
        case 6:         *to = *from++;
        case 5:         *to = *from++;
        case 4:         *to = *from++;
        case 3:         *to = *from++;
        case 2:         *to = *from++;
        case 1:         *to = *from++;
                } while(--n > 0);
        }
}

Поскольку оператор switch действительно просто вычисляет адрес и переходит к нему, легко понять, почему он может перекрываться с другими структурами управления; строки в других структурах управления также имеют адреса, которые могут быть также нацелены на достижение цели!

В представленном вами случае представьте, не было ли в вашем коде switch или break. Когда вы закончите выполнение then части инструкции if, вы просто продолжаете идти, поэтому вы попадаете в case 2:. Теперь, поскольку у вас есть switch и break, важно, из чего может выйти break. Согласно странице MSDN, "Операция C break" ,

Оператор break завершает выполнение ближайшего вложения do, для, или while, в котором он отображается. Элемент управления переходит к оператору, который следует за завершенным оператором.

Поскольку ближайший охватывающий do, для, переключатель или , а - ваш переключатель (обратите внимание, что , если не включен в этот список), тогда, если вы находитесь внутри блока then, вы переходите к внешней стороне оператора switch. Что еще более интересно, это то, что произойдет, если вы введете case 0, но c == 'A' - false. Затем if передает управление сразу после закрывающей скобки блока then, и вы начинаете выполнение кода в case 2.

Ответ 2

В C и С++ законно прыгать в циклы и блокировать до тех пор, пока вы не перепрыгнете через любые объявления переменных. Вы можете проверить этот ответ для примера, используя goto, но я не понимаю, почему те же идеи не будут применяться к блокам switch.

Семантика отличается от того, что } выше case 1, как и следовало ожидать.
Этот код на самом деле говорит, что если state == 0 и c != 'A', то перейдите к case 2 с тех пор, где закрывающая скобка оператора if. Затем он обрабатывает этот код и попадает в оператор break в конце кода case 2.