Evg |
СОДЕРЖАНИЕ
Как работает оператор switch в Си/Си++
Метки c++
ВНИМАНИЕ! Вопросы по существу обсуждаемого вопроса просьба задавать здесь или создать тему на форуме и кинуть на неё ссылку в блог или мне в личку.
Объясняю почему
Причин для этого несколько. Я, как и любой другой автор, всегда могу упустить интересный момент обсуждаемой темы (что подтвердилось на практике). А потому задаваемый вопрос может закрывать пробел в статье. Ответ на конкретный вопрос, как правило, дать несложно. Сложнее его аккуратно сформулировать так, чтобы ответ являлся законченной частью статьи. Поэтому, как правило, на первых порах я ограничиваюсь конкретным ответом на конкретный вопрос, а в статью временно вставляю ссылку на пост, где был дан ответ. А когда дойдут руки, то вместо ссылки пишу нормальное пояснение. Технические возможности блога не позволяют в комментариях пользоваться широкими возможностями, доступными на форуме (то как выделение текста жирным, вставка фрагментов исходников в удобном для чтения виде и т.п.), поэтому будет удобнее, если вопрос и ответ будут опубликованы на форуме Любая статья является изложением знаний в общем случае. У многих людей мышление устроено так, что прочтя на форуме конкретный вопрос и конкретный ответ на этот вопрос, у них появится бОльшее понимание, чем после прочтения теоретических выкладок (даже если они подкреплены конкретными примерами). Ссылки на такие обсуждения я, как правило, включаю в последний раздел статьи. Начинающие, как правило, поиск ответов на свои вопросы ведут именно в форуме, а не в блогах. А потому конкретный вопрос и конкретный ответ для них будет более удобным и полезным именно на форуме. Многие люди умеют работать методом тыка, лишь бы был конкретный пример в качестве образца. А потому такое обсуждение будет им полезным даже без прочтения статьи Исторически сложилось, что раньше (когда ещё не было блога) статьи располагались на форуме и представлены были в виде двух тем. Первая тема создавалась в специально отведённой свалке и представляла собой черновик, который со временем дорабатывался до законченной статьи. После этого статья переезжала во вторую тему в тематическом разделе. А первая тема оставалась дополнительной свалкой для замечаний и мелких вопросов по теме. Ссылку на старое местоположение данной свалки я помещаю в начале статьи. Вопросы, по возможности, прошу создавать в отдельных темах, но если вопрос действительно мелкий, то можно его задать и в указанной свалке. 1. Как обычно поясняется работа switch'а в учебниках Так или иначе все программисты считают, что они знают ответ на этот вопрос. Однако это не так. В большинстве случаев знания почерпаны из книг и учебников или являются эмпирическими результатами самостоятельных исследований. Но, как показала практика, большинство программистов (к их числу когда-то относился и я) на самом деле не знают точной семантики (смысла) оператора switch. Начну с простого примера C switch (x) { case 10: y = 1; break; case 11: case 12: y = 2; break; default: y = 3; break; } C if (x == 10) { y = 1; } else if (x == 11 || x == 12) { y = 2; } else { y = 3; } Те, кто перешёл с Паскаля на Си, очень часто спотыкаются о необходимость расставлять break'и. В паскале конструкция case (а точнее, её альтернатива, ибо я не помню, как правильно в паскале писать) означает автоматическое завершение кода предыдущей альтернативы и уход на конец switch'а. В учебниках сей факт как правило поясняют тем, что break можно не ставить, и в этом случае произойдёт провал (fall) на следующую альтернативу. Ну и как частный случай - два подряд идущих case'а являются вырожденным случаем провала. Необходимость построения настоящих (невырожденных) провалов на практике является статистически редким явлением, и потому многих необходимость написания break'а сильно раздражает. 2. Как на самом деле работает switch Оператор switch на самом деле является коммутируем переходом. Т.е. в зависимости от значения ключа (то, что стоит в скобках после слова switch) происходит переход на ту или иную метку, а операторы case определяют эти самые метки. Продемонстрирую это на примере, содержащем в том числе провалы (отсутствие break'ов). C switch (x) { <--- скобка 1 case 10: y = 1; break; case 11: case 12: y = 2; /* break; */ <--- тут уберём break и устроим провал default: y = 3; break; } <--- скобка 2 C /* Данный набор операторов if и goto есть семантический эквивалент * оператора switch (x) */ if (x == 10) goto L10; else if (x == 11) goto L11; else if (x == 12) goto L12; else goto Ldefault; { <--- скобка 1 L10: <--- метка "case 10" y = 1; goto Finish; <--- break L11: <--- метка "case 11" L12: <--- метка "case 12" y = 2; /* goto Finish; */ <--- закомментированный break Ldefault: <--- метка "default" y = 3; goto Finish; <--- break } <--- скобка 2 Finish: <--- метка за пределами switch'а, куда ведут все break'и эта метка полностью эквивалентна тому, что есть в циклах for и while 3. То, о чём редко пишут в учебниках Поискав через google описание работы switch'а, я много раз натыкался на описание синтаксиса. Этот синтаксис сводится к следующему (здесь привожу вариант с MSDN): Код:
switch (expression) { case constant_expression_1 : statement_1; ... case constant_expression_n : statement_n; [default: statement_n+1] } Код:
if (expression) body Код:
while (expression) body Код:
switch (expression) body Теперь рассмотрим синтетический (т.е. не из реальной жизни) пример, который на первый взгляд многим может показаться диким, потому что идёт в разрез с имеющимся представлением о работе switch'а: C switch (x) if (z == 5) { case 10: y = 1; } else { case 11: if (z > 10) y = 2; else { default: y = 3; } } C if (x == 10) <--- начало оператора switch goto L10; else if (x == 11) goto L11; else goto Ldefault; <--- конец оператора switch if (z == 5) <--- начало "body" { L10: <--- метка "case 10" y = 1; } else { L11: <--- метка "case 11" if (z > 10) y = 2; else { Ldefault: <--- метка "Ldefault" y = 3; } } C switch (x) { int a, b; <--- переменные a и b доступны только внутри фигурных скобок case 10: a = x + 1; b = x - 1; y = a * b; break; ... } C void send (int *to, const int *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. Для чего придумали оператор switch С виду кажется, что switch в "нормальном" случае принципиально ничем не отличается от цепочки конструкций if - else if - ... По принципу действия действительно не отличается. Однако с точки зрения генерации кода switch принципиально строится по другому. Проще всего это опять пояснить на коротком примере. C switch (x) { case 5: y = 10; case 6: y = 25; case 7: y = 38; ... case 20: y = 125; default: y = 1000; } C /* Массив меток перехода на 16 альтернатив switch'а (от 5 до 20) */ static void *arr[16] = { &&L5, &&L6, &&L7, ..., &&L20 }; /* При таких значениях x мы попадаем в один из case'ов switch'а, * адрес для перехода загрузим из таблицы arr. При этом надо понимать, что * нулевой элемент таблицы соответствует значению x = 5. */ if (x >= 5 && x <= 20) goto *arr[x-5]; else goto L_default; L5: y = 10; L6: y = 25; L7: y = 38; ... L20: y = 125; L_default: y = 1000; Есть некоторые тонкие моменты, остающихся на усмотрение компилятора. Например, если в switch'е мы имеем всего две альтернативы 1 и 1000000, то таблица, построенная по такому принципу, занимала бы миллион слов (4 мегабайта в 32-битном режиме). Компилятор в таком случае вместо динамического перехода построит цепочку из двух if'ов. Есть ещё куча аналогичных моментов, которые я не стану описывать, потому что для общего понимания предназначения switch'а эти моменты не являются критичными 5. Ссылки на темы, где обсуждался данный вопрос |
Всего комментариев 24
Комментарии
-
[B]Pure[/B], дали же грамотный совет:
[QUOTE]можешь писать так для себя так, как хочешь. Главное, другим не показывай — засмеют ведь.[/QUOTE]
просто придерживайся его и всё у тебя будет хорошоЗапись от LosAngeles размещена 04.05.2012 в 06:56 -
Запись от Pure размещена 04.05.2012 в 21:49 -
Запись от vitaliy2034 размещена 27.05.2017 в 12:41 -
Ещё один интересный пример поместил в https://www.cyberforum.ru/cpp-... st12288952
Запись от Evg размещена 08.04.2018 в 11:34