Почему оператор switch был предназначен для перерыва? - PullRequest
128 голосов
/ 31 октября 2008

С учетом простого оператора switch

switch (int)
{
    case 1 :
    {
        printf("1\n");
        break;
    }

    case 2 : 
    {
        printf("2\n");
    }

    case 3 : 
    {
        printf("3\n");
    }
}

Отсутствие оператора break в случае 2 означает, что выполнение будет продолжено внутри кода для случая 3. Это не случайность; это было разработано таким образом. Почему были приняты эти решения? Какую пользу это дает по сравнению с автоматическим семантиком разрыва для блоков? Каково было обоснование?

Ответы [ 9 ]

145 голосов
/ 31 октября 2008

Многие ответы, кажется, сосредоточены на способности провалиться как причина , требующая утверждения break.

Я полагаю, что это была просто ошибка, во многом потому, что когда был разработан C, опыта использования этих конструкций было не так много.

Питер Ван дер Линден приводит пример в своей книге «Программирование на Expert C»:

Мы проанализировали источники компилятора Sun C чтобы увидеть, как часто падение по умолчанию был использован. Солнце ANSI C передний конец компилятора имеет переключатель 244 заявления, каждое из которых имеет в среднем из семи случаев. Провалиться происходит только в 3% всех этих случаев.

Другими словами, нормальный переключатель поведение неправильно 97% времени. Это не только в компиляторе - на наоборот, где провал был использован в этом анализе это было часто для ситуации, которые встречаются чаще в компиляторе, чем в другом программном обеспечении, например, при компиляции операторов которые могут иметь один или два Операнды:

switch (operator->num_of_operands) {
    case 2: process_operand( operator->operand_2);
              /* FALLTHRU */

    case 1: process_operand( operator->operand_1);
    break;
}

Дело проваливается так широко признан недостатком, что есть даже специальное соглашение о комментариях, показано выше, что говорит Линт "это на самом деле один из тех 3% случаев, когда провал был желателен. "

Я думаю, для C # было бы хорошей идеей требовать явного оператора перехода в конце каждого блока падежей (при этом все же позволяя объединять несколько меток регистров - до тех пор, пока существует только один блок инструкций). В C # вы все равно можете сделать так, чтобы один случай падал на другой - вам просто нужно сделать явное падение, перейдя к следующему случаю, используя goto.

Жаль, что Java не воспользовалась возможностью оторваться от семантики Си.

30 голосов
/ 31 октября 2008

Во многих отношениях c - это просто чистый интерфейс для стандартных идиом сборки. При написании управления потоком на основе таблицы переходов у программиста есть выбор - провалиться или выпрыгнуть из «структуры управления», и для перехода требуется явная инструкция.

Итак, c делает то же самое ...

21 голосов
/ 31 октября 2008

Чтобы реализовать устройство Даффа, очевидно:

dsend(to, from, count)
char *to, *from;
int count;
{
    int 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);
    }
}
18 голосов
/ 31 октября 2008

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

case 0:
case 1:
case 2:
    // all do the same thing.
    break;
case 3:
case 4:
    // do something different.
    break;
default:
    // something else entirely.

Если бы коммутатор был спроектирован так, чтобы неявно срабатывать после каждого случая, у вас не было бы выбора. Конструкция распределительного щита была разработана таким образом, чтобы она была более гибкой.

15 голосов
/ 31 октября 2008

Операторы case в операторах switch являются просто метками.

Когда вы включаете значение, оператор switch по существу делает goto для метки с соответствующим значением.

Это означает, что разрыв необходим, чтобы избежать перехода к коду под следующей меткой.

Что касается причины , почему был реализован таким образом - сквозной характер оператора switch может быть полезен в некоторых сценариях. Например:

case optionA:
    // optionA needs to do its own thing, and also B's thing.
    // Fall-through to optionB afterwards.
    // Its behaviour is a superset of B's.
case optionB:
    // optionB needs to do its own thing
    // Its behaviour is a subset of A's.
    break;
case optionC:
    // optionC is quite independent so it does its own thing.
    break;
8 голосов
/ 13 ноября 2010

Чтобы разрешить такие вещи, как:

switch(foo) {
case 1:
    /* stuff for case 1 only */
    if (0) {
case 2:
    /* stuff for case 2 only */
    }
    /* stuff for cases 1 and 2 */
case 3:
    /* stuff for cases 1, 2, and 3 */
}

Думайте о ключевом слове case как о ярлыке goto, и оно появляется намного естественнее.

3 голосов
/ 31 октября 2008

Устраняет дублирование кода, когда нескольким случаям необходимо выполнить один и тот же код (или один и тот же код в последовательности).

Поскольку на уровне ассемблера не важно, будете ли вы разбивать между ними или нет, в любом случае не будет никаких накладных расходов на падение при рассмотрении дел, так почему бы не разрешить их, поскольку в некоторых случаях они дают значительные преимущества.

2 голосов
/ 14 августа 2018

Мне довелось столкнуться со случаем назначения значений в векторах структурам: это должно было быть сделано таким образом, чтобы, если вектор данных был меньше числа элементов данных в структуре, остальные члены останется в их значении по умолчанию. В этом случае опущение break было весьма полезным.

switch (nShorts)
{
case 4: frame.leadV1    = shortArray[3];
case 3: frame.leadIII   = shortArray[2];
case 2: frame.leadII    = shortArray[1];
case 1: frame.leadI     = shortArray[0]; break;
default: TS_ASSERT(false);
}
0 голосов
/ 31 октября 2008

Как многие здесь указали, это позволяет одному блоку кода работать для нескольких случаев. Это должно быть более распространенным явлением для ваших операторов switch, чем "блок кода для случая", который вы указываете в своем примере.

Если у вас есть блок кода на случай без провала, возможно, вам следует рассмотреть возможность использования блока if-elseif-else, так как это может показаться более подходящим.

...