Многие ответы, кажется, сосредоточены на способности провалиться как на причине требования break
утверждения.
Я считаю, что это была просто ошибка, во многом из-за того, что при разработке C не было такого большого опыта в использовании этих конструкций.
Питер Ван дер Линден приводит доводы в своей книге «Экспертное программирование на языке Си»:
Мы проанализировали исходные тексты компилятора Sun C, чтобы увидеть, как часто использовались провалы по умолчанию. Передняя часть компилятора Sun ANSI C имеет 244 оператора переключения, каждый из которых имеет в среднем семь случаев. Падение происходит всего в 3% всех этих случаев.
Другими словами, нормальное поведение переключателя неверно в 97% случаев. Дело не только в компиляторе - напротив, там, где в этом анализе использовался провал, это часто было для ситуаций, которые чаще возникают в компиляторе, чем в другом программном обеспечении, например, при компиляции операторов, которые могут иметь один или два операнда. :
switch (operator->num_of_operands) {
case 2: process_operand( operator->operand_2);
case 1: process_operand( operator->operand_1);
break;
}
Провал кейса настолько широко признан как дефект, что существует даже специальное соглашение о комментариях, показанное выше, в котором говорится, что lint «это действительно один из тех 3% случаев, когда провал был желательным».
Я думаю, что для C # было хорошей идеей требовать явного оператора перехода в конце каждого блока case (при этом позволяя складывать несколько меток case - до тех пор, пока существует только один блок операторов). В C # один случай может переходить в другой - вам просто нужно сделать это явным, перейдя к следующему случаю с помощью файла goto
.
Жаль, что Java не воспользовалась возможностью вырваться из семантики C.
Во многих отношениях c - это просто чистый интерфейс для стандартных идиом сборки. При написании управления потоком, управляемым таблицей переходов, программист может выбрать падение или выпрыгивание из «структуры управления», а для перехода требуется явная инструкция.
Итак, c делает то же самое ...
источник
NULL
. (Продолжение в следующем комментарии.)NUL
-терминальные строки - едва ли не худшая идея.DO
.Очевидно, что для реализации устройства Даффа:
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); } }
источник
Если бы кейсы были спроектированы так, чтобы неявно ломаться, у вас не могло бы быть провала.
case 0: case 1: case 2: // all do the same thing. break; case 3: case 4: // do something different. break; default: // something else entirely.
Если бы переключатель был спроектирован так, чтобы неявно срабатывать после каждого случая, у вас не было бы выбора. Конструкция корпуса выключателя была спроектирована так, чтобы быть более гибкой.
источник
Операторы case в операторах switch - это просто метки.
При включении значения, оператор переключатель по существу делает Гот на метку со значением соответствия.
Это означает, что разрыв необходим, чтобы избежать перехода к коду под следующей меткой.
Что касается причины, по которой это было реализовано таким образом, то в некоторых сценариях может быть полезен провальный характер оператора 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;
источник
break
где нужно. 2) Если порядок операторов case был изменен, провал может привести к запуску неправильного case. Таким образом, я считаю, что C # обрабатывает намного лучше (явноgoto case
для провалов, за исключением пустых меток case).break
в C # тоже есть способы забыть - самый простой - это когда вы не хотите, чтобы acase
делал что-либо, но забываете добавитьbreak
(возможно, вы так увлеклись пояснительным комментарием или вас перезвонили по какому-то другая задача): выполнение просто упадет доcase
следующего. 2) обнадеживаетgoto case
поощрение неструктурированного "спагетти" кодирования - вы можете получить случайные циклы (case A: ... goto case B; case B: ... ; goto case A;
), особенно когда случаи разделены в файле и их трудно найти в сочетании. В C ++ локализован провал.Чтобы разрешить такие вещи, как:
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
ярлыке, и это получается намного естественнее.источник
if(0)
в конце первого случая является злом, и должно использоваться только в том случае, если целью является запутывание кода.Это исключает дублирование кода, когда в нескольких случаях необходимо выполнить один и тот же код (или один и тот же код последовательно).
Поскольку на уровне языка ассемблера не важно, прерываете ли вы между каждым из них или нет, в любом случае накладные расходы на провалы в случаях нулевые, так почему бы не разрешить их, поскольку они предлагают значительные преимущества в определенных случаях.
источник
Мне довелось столкнуться со случаем присвоения значений в векторах структурам: это должно было быть сделано таким образом, чтобы, если вектор данных был короче, чем количество элементов данных в структуре, остальные члены остались бы в их значение по умолчанию. В этом случае исключение
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); }
источник
Как многие здесь указали, это позволяет одному блоку кода работать в нескольких случаях. Это должно быть более частым явлением для ваших операторов switch, чем «блок кода на случай», который вы указываете в своем примере.
Если у вас есть блок кода для каждого случая без провалов, возможно, вам следует рассмотреть возможность использования блока if-elseif-else, поскольку это может показаться более подходящим.
источник