Подтвердить что ты не робот

Почему было сделано предложение о переключении?

Учитывая простой оператор switch

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

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

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

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

4b9b3361

Ответ 1

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

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

Питер Ван дер Линден делает дело в своей книге "Expert C Programming":

Мы проанализировали источники компилятора 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;
}

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

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

Слишком плохо, что Java не воспользовалась возможностью, чтобы выйти из семантики C.

Ответ 2

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

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

Ответ 3

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

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

Ответ 4

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

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

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

Ответ 5

Операторы case в операторах switch - это просто метки.

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

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

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

case optionA:
    // optionA needs to do its own thing, and also B 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;

Ответ 6

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

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, и он будет намного более естественным.

Ответ 7

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

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

Ответ 8

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

Ответ 9

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

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