LINUX.ORG.RU

C++, switch, case ... что-то я туплю

 ,


0

3

Редко пользуюсь оператором switch, хотя думал, что знаю как он работает. Обычно я обязательно пользуюсь break, и switch тогда работает ожидаемо. Но вот я сделал примитивный пример, в котором не могу понять что происходит.

#include <iostream>

using namespace std;

int main (int argc, char *argv[]) 
{
    int i=0;

    switch (i)
    {
    	case 0  : { std::cout << "0"; }
        case 1  : { std::cout << "1"; }
      	default : { std::cout << "D"; }
    }

    return 0;
}


Что выведет этот пример?

Вначале я думал что «0».

Потом не смог найти внятного описания default - выполняется ли это условие только если не подошло ничего из остального, или просто выполняется если до него дошло дело. Я склонился к тому, что default выполняется просто если до него дошло дело (небыло никаких break). Поэтому результат должен быть «0D».

На деле на gcc результат будет таким: «01D».

Два вопроса в связи с этим:

- С какого перепугу в результате затесалось 1?
- Как же все-таки должно срабатывать default?

★★★★★

Последнее исправление: Xintrea (всего исправлений: 2)

break забыл. default - unmatched case. Аналог - else.

Не должно быть 0D.

У тебя сматчился 0, выполнился кейс на 0, нет брейка, выполнился кейс 1, нет брейка, выполнился кейс defaul.

И завязывай править пост. Я устал править сообщение.

Deleted
()
Последнее исправление: Deleted (всего исправлений: 4)
Ответ на: комментарий от Deleted

break я специально не написал. Я хочу проверить, как выполняется поток команд.

Для начала вопрос в единице. Условие «case 1:» в строке есть (это не if, но это тоже условие), так какого хрена эта строка выполняется, если i=0?

Xintrea ★★★★★
() автор топика

case работают как goto метки. Если нет break, исполнение «проваливается» в следующий case.

i-rinat ★★★★★
()

А с какого перепугу оно должно работать как-то иначе?

switch(message_id) {
  case MES_ID1:
  case MES_ID2:
    //do somthing
    break;
  case MES_ID3:
   // do_somthing
   break;
  default;
    //do_somthing
    break;
}

Понимаешь например, что делает такой пример? Для чего используется? И как бы он работал по твоей «логике»?

А вообще б не гадал бы ты, а почитал доку.

http://en.cppreference.com/w/cpp/language/switch

Dudraug ★★★★★
()
Последнее исправление: Dudraug (всего исправлений: 1)
Ответ на: комментарий от Xintrea

Считай что это входное условие в поток.

Твой код - поток:
- выведи 0
- выведи 1
- выведи D;
Если i = 0, то мы попадаем в первый пункт и выполняем поток по пунктам до конца.
При = 1, во второй пункт, при любых других только в D.
Сам поток не оперирует условиями. Условия определяют только точку входа в поток.

Deleted
()
Ответ на: комментарий от Deleted

Да, я понял. В общем исполнение идет от первого сматченного условия невзирая на все последующие условия до конца switch или до того как встретится break.

Вот это меня удивляло, почему исполнение идет невзирая на другие условия.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

почему исполнение идет невзирая на другие условия.

Так сложилось исторически, у k&r были какие-то свои причины для этого.

Twissel ★★★★★
()
Ответ на: комментарий от Xintrea

Вот это меня удивляло, почему исполнение идет невзирая на другие условия.

Что бы не удивляться неплохо б подучить язык и инструменты которыми пользуешься

Dudraug ★★★★★
()
Ответ на: комментарий от Deleted

Считай что это входное условие в поток.
Сам поток не оперирует условиями. Условия определяют только точку входа в поток.

Вот это хорошее объяснение. Возможно, авторы языка хотели сделать именно это. Но все объясняют работу оператора исходя из каких-то других соображений.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

идет невзирая на другие условия.

Дядя, очень толсто.
Так заимплеменчено. Какой иначе смысл в switch case?
Ты хочешь что-то типа:

val a: String;
switch (a) {
  case a.length > 2 -> //do 1
  case a.length < 4  -> //do 2
}

И что бы при
a.length == 3
выполнялось и 1 и 2?

Deleted
()
Ответ на: комментарий от Dudraug

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

Xintrea ★★★★★
() автор топика

use break, Luke!

#include <iostream>

using namespace std;

int main (int argc, char *argv[]) 
{
    int i=0;

    switch (i)
    {
    	case 0  : { std::cout << "0"; break; }
        case 1  : { std::cout << "1"; break; }
      	default : { std::cout << "D"; break; }
    }

    return 0;
}
bvn13 ★★★★★
()
Ответ на: комментарий от Xintrea

И свитчи без бреака не пишу.

как положено

Смешно, я выше привел пример, что код без break; может быть вполне правильным и корректным при определенных требованиях. Код БЕЗ break; не является НЕПРАВИЛЬНЫМ, а код с ним ПРАВИЛЬНЫМ. Просто есть специфика того как работает switch-case и понимания этого у тебя нет. Как бы ты описал мой пример выше с знаниями о switch-case до создания этого треда?

Dudraug ★★★★★
()
Последнее исправление: Dudraug (всего исправлений: 1)
Ответ на: комментарий от Deleted

Дядя, очень толсто. Так заимплеменчено. Какой иначе смысл в switch case?

Я не знаю, какой смысл. Я хочу его найти. Вот тут дали ссылку на доку:

http://en.cppreference.com/w/cpp/language/switch

Ну и толку с этой доки? Там написано что делает команда, особенности синтаксиса. А вот смысл данной команды не написан.

Ты же сам написал про поток и точку входа. В этом объяснении смысла больше, чем на всей странице cppreference.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

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

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от Deleted

Код правильный. Свитчи без брейка в нищих языка - это норма.

Да понятно что может быть и правильным, и есть уникумы, которые используют в коде свитчи без бреака намеренно. Ну коль язык позволяет, чего бы не использовать такую прекрасную возможность. И код даже работать будет.

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

Xintrea ★★★★★
() автор топика

вы фигурным скобкам придаете больше значимости чем есть на самом деле....

так более понятно, что и почему

switch (i)
{
    	case 0:  std::cout << "0";
        case 1:  std::cout << "1";
      	default: std::cout << "D";
}
anonymous2 ★★★★★
()
Последнее исправление: anonymous2 (всего исправлений: 1)
Ответ на: комментарий от Deleted

Давай уж тогда договаривать до конца. При этом они требуют оформить такой case специальным комментарием.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Я тебе выше привел вполне нормальный и стандартный пример switch-case без break, который лаконичный, понятный и эффективный. И как раз уникумы те, кто такое не используют и не понимают. Но ты решил этот пример проигнорить, молодец чо, дальше создавай мир программистов в своей голове.

Dudraug ★★★★★
()
Ответ на: комментарий от Xintrea

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

Сказал человек, который еще час назад не понимал как работает switch-case, а уже с умным видом рассуждает о код-стайле и потнециальных ошибках.

Dudraug ★★★★★
()

Вот тебе еще на «пораскинуть мозгами» (толи в саперском понимании, толи в традиционном):

  • Простой:
    int i = 0;
    switch (i) {
    case 0: { std::cout << "0"; }
    default: { std::cout << "D"; }
    case 1: { std::cout << "1"; }     	
    }
    
  • Сложный (courtesy of govnokod.ru):
    int i = 5;
    switch (2) {
        case 0:
            for (i = 0; i < 10; i++) {
        case 1:
            printf("A i=%d\n", i);
        case 2:
            printf("B i*i=%d\n", i * i);
            }
        case 3:
            printf("done");
            break;
    }
    
KennyMinigun ★★★★★
()
Последнее исправление: KennyMinigun (всего исправлений: 1)
Ответ на: комментарий от Dudraug

Твой пример вполне уместен, когда пишешь что-то типа машины состояний, и тебе нужно комплексно (или накопительно) обрабатывать эти состояния.

С комплексной обработкой (как у тебя, когда case не имеет операторов) проблем нет. А вот уже с накопительной обработкой, когда case имеет операторы, но не завершается break, рано или поздно возникнет проблема, уж поверь.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Dudraug

Кстати, господа, у нас ресурс называется «Русская информация об ОС Linux». А ссылки даете на англоязычные ресурсы, хотя то же самое есть на русском. Нехорошо.

http://ru.cppreference.com/w/cpp/language/switch

Xintrea ★★★★★
() автор топика

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

conalex ★★★
()
Последнее исправление: conalex (всего исправлений: 2)
Ответ на: комментарий от Xintrea

А ссылки даете на англоязычные ресурсы, хотя то же самое есть на русском. Нехорошо.

Блин, ты специально говоришь полную фигню не подумав? Можно ж было промолчать и не показывать, что твой уровень понимания происходящего еще ниже чем все думали. Российской цппреференс можно разве что школоте для уроков информатики советовать. Перевод там УЖАСНЕЙШИЙ, который тупо коверкает и упускает важнейшие ньюансы, да и часть инфы отсутствует.

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

Dudraug ★★★★★
()
Ответ на: комментарий от Xintrea

Если expression вычисляет значение, равное значению одного из определено constant_expressioni, statementi (если имеется) и все последующие заявления (за исключением default_statement, если он присутствует) выполняются.

Чот проиграл с такого.

Dudraug ★★★★★
()
Ответ на: комментарий от Xintrea

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

АХАХАХАХАХА

Dudraug ★★★★★
()
Ответ на: комментарий от Xintrea

http://en.cppreference.com/w/cpp/language/switch
Ну и толку с этой доки? Там написано что делает команда, особенности синтаксиса. А вот смысл данной команды не написан.

Ты издеваешься, что ли?! Там всё в деталях описано, с примерами. Написано, что выполнение проваливается, есть пример кода и его результата исполнения. Причём, описано это несколько раз. Это надо очень постараться, чтобы пропустить.

i-rinat ★★★★★
()

Хм, так это есть в любой книжке по С/С++ для нубов. ИМХО, это ещё цветочки по сравнению с move/copy конструкторами, а ведь есть наверняка вещи ещё сложнее.

Deleted
()
Ответ на: комментарий от Deleted

ИМХО, это ещё цветочки по сравнению с move/copy конструкторами, а ведь есть наверняка вещи ещё сложнее.

А что там сложного? O_o

Dudraug ★★★★★
()

понимаю бро

проживание в Волгодонске накладывает отпечаток на психику поцие

сочувствую бро

icefreeze
()
Ответ на: комментарий от Dudraug

А что там сложного? O_o

После того, как понял — ничего. А вот как раз вьехать без нормального обьяснения и примеров сложно.

KennyMinigun ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Не надо тут микроскопами бросаться!

По теме: есть языки (не помню, но точно есть) где по умолчанию при выполнении case автоматически выходит из конструкции.

Тут же (магу и ашыбацца): логика была «с какого места вход». таким образом рисовалась более глубокая нора.

Жаль, был годный пример про строки и склонение. Так вот там не использовался break. А точка входа. таким образом строился вполне адекватный ответ пользователю.

Deleted
()
Ответ на: комментарий от i-rinat

Повторюсь: в нете я нашел (в своё время, ссылок не дам) только
несколько примеров «годного» применения проваливания в нору.
Я так понимаю, человек задаётся вопросом «Почему так, а не эдак?»

Deleted
()

case 0, case 1 и default - это просто метки, switch просто делает goto на нужную метку, дальше код выполняется линейно. Кстати, они могут стоять в любом порядке, например, default вначале, а остальные вперемешку.

P.S. Для твоего исходника компилятор должен был выдать предупреждение.

anonymous
()
Ответ на: комментарий от Xintrea

case это такой jump к метке которая совпадает со значением переменной переданной в этот оператор.

Следовательно после того, как тебя перебросит к указанной метке, выполнится весь код после нее. Break выкинет тебя за тело case.

Зачем есть возможность не писать break.

Предположим у тебя есть:

enum Type 
{
    TypeA, 
    TypeB, 
    TypeC
};

Где-то в коде в какой-то процедуре, тебе нужно выполнить что-то одинаковое и для TypeA и для TypeB поэтому можно написать так:

Type type = getType();

case (type)
{
    case TypeA:
    case TypeB:
        doSomethingForBothTypes();
        break;
    case TypeB:
        doSomethingForTypeCOnly();
        break;
    default:
        // а этот код никогда не выполнится, т.к. мы все кейсы обработали
        break;
}
pozitiffcat ★★★
()
Ответ на: комментарий от no-such-file

Загугли duff's device и сломай себе мозг.

Там все примитивно, если понять, что switch, for, while нужно расписать через if и goto на метки, а затем понять, что на самом дели они именно так и работают.

anonymous
()

Интересно, многие ли готовы предсказать выполнение кода типа

switch (0)
  puts("Hello");
или
switch (1) {
  int i = 0;
  case 1: printf("%d", ++i);
}
В целом, создатели C не заморачивались делать читаемый язык %)

anonymous
()
Ответ на: комментарий от KennyMinigun

Сложный

int i = 5;
switch (2) {
    case 0:
        for (i = 0; i < 10; i++) {
    case 1:
        printf("A i=%d\n", i);
    case 2:
        printf("B i*i=%d\n", i * i);
        }
    case 3:
        printf("done");
        break;
}

А так оно тоже сложный?

    int i = 5;
    switch (2) {
        case 0:
            // for (i = 0; i < 10; i++) {
            i = 0;
            label: if( i >= 10 ) goto end;
        case 1:
            printf("A i=%d\n", i);
        case 2:
            printf("B i*i=%d\n", i * i);
            ++i;
            goto label;
            // }
            end:
        case 3:
            printf("done");
            break;
    }

anonymous
()
Ответ на: комментарий от Xintrea

А вот уже с накопительной обработкой, когда case имеет операторы, но не завершается break, рано или поздно возникнет проблема, уж поверь.

Вот поэтому в таких случаях компилятор выдает предупреждение, а для его подавления придумали атрибут [[fallthrough]].

anonymous
()
Ответ на: комментарий от anonymous
switch (1) {
  int i = 0;
  case 1: printf("%d", ++i);
}

Почему тут пропускается int i = 0; я еще понимаю, но почему printf() не отрабатывает, ведь переход на case 1 происходит (даже если заменить ++i на что-то определенное, чтобы не было UB и собирать без оптимизации)?

P.S. Такому коду, конечно не место в коммерческой разработке, но все же почему он не выводит printf()?

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.