Щоб додати відповіді тут, я вважаю, що варто розглянути протилежне питання разом із цим, а саме. чому C в першу чергу дозволив пропустити?
Будь-яка мова програмування звичайно служить двом цілям:
- Надайте інструкції комп’ютеру.
- Залиште запис про наміри програміста.
Тому створення будь-якої мови програмування - це баланс між тим, як найкраще виконати ці дві цілі. З одного боку, тим простіше перетворитись на інструкції на комп'ютері (будь то машинний код, байт-код, наприклад, IL, чи інструкції інтерпретуються при виконанні), тим більш спроможним буде процес компіляції чи інтерпретації бути ефективним, надійним та компактний у виробництві. Доведена до крайньої мети, ця мета призводить до того, що ми просто писали в збірці, IL або навіть необроблені оп-коди, тому що найпростіша компіляція - там, де немає компіляції взагалі.
І навпаки, чим більше мова виражає наміри програміста, а не засоби, використані для цього, тим зрозуміліша програма як під час написання, так і під час обслуговування.
Тепер, switch
його завжди можна було скласти, перетворивши його в еквівалентний ланцюжок if-else
блоків або подібне, але він був розроблений як такий, що дозволяє компілювати в певний загальний шаблон складання, коли людина приймає значення, обчислює зсув з нього (чи то шукаючи таблицю індексується досконалим хешем значення або фактичною арифметикою значення *). На цьому етапі варто відзначити, що сьогодні компіляція C # іноді перетворюється switch
на еквівалент if-else
, а іноді використовує хеш-підхідний стрибок (а також з C, C ++ та іншими мовами із порівнянним синтаксисом).
У цьому випадку є дві вагомі причини, що дозволяють пропускати:
Так чи інакше буває природно: якщо ви вбудуєте таблицю стрибків у набір інструкцій, і одна з попередніх груп інструкцій не містить певного стрибка чи повернення, то виконання просто природним чином перейде до наступної партії. Дозвіл пропускання - це те, що "просто трапиться", якби ви повернулиswitch
використовуючи C на таблицю стрибків за допомогою машинного коду.
Кодери, які писали в асемблері, вже були використані в еквіваленті: коли писати таблицю стрибків вручну в зборі, вони повинні були б врахувати, чи закінчиться даний блок коду поверненням, стрибком поза таблицею, або просто продовжуватиме далі до наступного блоку. Таким чином, надання кодеру додати явне, break
коли це було необхідне, було "природним" і для кодера.
Тому в той час це була розумна спроба збалансувати дві цілі комп'ютерної мови, оскільки вона стосується як виробленого машинного коду, так і виразності вихідного коду.
Через чотири десятиліття все не зовсім однакове з кількох причин:
- Сьогодні кодери на C можуть мати мало досвіду монтажу або взагалі ніякого. Кодери в багатьох інших мовах стилю С є ще рідше (особливо Javascript!). Будь-яке поняття "до чого звикли люди з монтажу" вже не є актуальним.
- Поліпшення оптимізації означають, що ймовірність того, що
switch
вони будуть перетвореніif-else
що він оскільки він вважав, що підхід, ймовірно, є найбільш ефективним, або ж перетвориться на особливо езотеричний варіант підходу до стрибків. Відображення між підходами вищого та нижчого рівня не настільки сильне, як раніше.
- Досвід показує, що пропускний процес має тенденцію до меншості, а не норми (дослідження компілятора Sun показало, що 3%
switch
блоків використовували пропуск, крім кількох міток у тому ж блоці, і вважалося, що використання випадок тут означав, що цей 3% був насправді набагато вищий за норму). Таким чином, вивчена мова робить незвичайне більш легким, ніж загальне.
- Досвід показує, що пропускне середовище, як правило, є джерелом проблем як у випадках, коли це відбувається випадково, так і у випадках, коли хтось підтримує код, пропускає правильний пропуск. Останнє є тонким доповненням до помилок, пов’язаних із пропусканням, тому що навіть якщо ваш код ідеально відсутній, помилки можуть все-таки спричинити проблеми.
Зв'язані з останніми двома пунктами, розгляньте наступну цитату з поточного видання K&R:
Проникнення від одного випадку до іншого не є надійним, воно схильне до дезінтеграції, коли програма модифікується. За винятком декількох міток для одного обчислення, пропускні дані повинні використовуватися скупо та коментуватися.
Як добру форму, поставте перерву після останнього випадку (тут за замовчуванням), навіть якщо це логічно непотрібно. Коли-небудь, коли в кінці буде додано інший випадок, цей захисний програмування врятує вас.
Отже, з вуст коня просипати в С проблематично. Доброю практикою вважається завжди документувати пропускні коментарі з коментарями, що є застосуванням загального принципу, який слід документувати, коли хтось робить щось незвичне, оскільки саме це пізніше вивчить код та / або зробить ваш код схожим на нього є помилка початківця в ньому, коли він насправді правильний.
І коли ви думаєте про це, кодуйте так:
switch(x)
{
case 1:
foo();
/* FALLTHRU */
case 2:
bar();
break;
}
Є чи додати що - то , щоб зробити падіння-через явне в коді, це просто не те , що можна виявити (або відсутність яких може бути виявлений) компілятором.
Таким чином, той факт, що він має бути явним із проривом у C #, не додає жодних покарань людям, які добре писали на інших мовах С-стилю, так як вони вже були б явними у своїх проривах. †
Нарешті, використання goto
тут уже є нормою C та інших таких мов:
switch(x)
{
case 0:
case 1:
case 2:
foo();
goto below_six;
case 3:
bar();
goto below_six;
case 4:
baz();
/* FALLTHRU */
case 5:
below_six:
qux();
break;
default:
quux();
}
У такому випадку, коли ми хочемо, щоб блок був включений у код, виконаний для значення, відмінного від того, яке приносить один до попереднього блоку, тоді нам вже доведеться використовувати goto
. (Звичайно, існують засоби та способи уникнути цього з різними умовами, але це стосується майже всього, що стосується цього питання). Як такий C # побудований на вже звичайному способі вирішення однієї ситуації, коли ми хочемо потрапити на більше ніж один блок коду в a switch
, а просто узагальнити його, щоб охопити також провал. Це також зробило обидва випадки зручнішими та самодокументуваннями, оскільки ми маємо додати нову мітку на C, але можемо використовувати case
як мітку в C #. У C # ми можемо позбутися below_six
етикетки і використовувати, goto case 5
що зрозуміліше, що ми робимо. (Ми також повинні були додатиbreak
для default
, який я пропустив лише для того, щоб вищезазначений код С явно не був C # кодом).
Таким чином:
- C # більше не стосується неоптимізованого виводу компілятора так само безпосередньо, як C-код 40 років тому (а також C цього дня), що робить одне з натхнень провалу неважливим.
- C # залишається сумісним з C, не просто маючи на увазі
break
, для легшого вивчення мови особами, знайомими з подібними мовами, та легшим переносом.
- C # видаляє можливе джерело помилок або неправильно зрозумілий код, який добре зафіксований як спричинення проблем протягом останніх чотирьох десятиліть.
- C # робить існуючу кращу практику з C (падіння документа) примусовою для виконання компілятором.
- C # робить незвичайний випадок тим, у кого є більш явний код, а звичайний - з кодом, який записується автоматично.
- C # використовує той самий
goto
підхід для потрапляння на той самий блок з різних case
міток, який використовується у C. Це просто узагальнює його в деяких інших випадках.
- C # робить цей
goto
підхід на основі більш зручним і зрозумілішим, ніж це в C, дозволяючи case
операторам виступати як мітки.
Загалом, досить розумне дизайнерське рішення
* Деякі форми BASIC дозволять робити те, що подобається, GOTO (x AND 7) * 50 + 240
хоча хоч крихкий і, отже, особливо переконливий випадок заборони goto
, слугує для показу вищого мовного еквіваленту такого способу, на якому код нижчого рівня може здійснити стрибок на основі арифметика за значенням, яке набагато розумніше, коли це результат компіляції, а не те, що потрібно підтримувати вручну. Реалізація пристрою Даффа, зокрема, добре піддається еквівалентному машинному коду або IL, оскільки кожен блок інструкцій часто буде однакової довжини, не потребуючи додавання nop
наповнювачів.
† Пристрій Даффа з'являється тут знову, як розумний виняток. Той факт, що з такою та подібними зразками відбувається повторення операцій, дозволяє зробити пропуск відносно зрозумілим навіть без явного коментаря до цього ефекту.