Конструкция switch — case в C++

12-10-21 C/C++ 10

Сегодня мы научимся пользоваться этой полезной конструкцией языка c++.

Очень часто в процессе написания программы требуется писать длинные if-else конструкции, например, когда мы получаем какой-либо ключ от пользователя; если вы пишете игру, то придется проверять на какую кнопку нажал игрок (вправо, влево, пробел и т.д.).

В этой статье мы узнаем как удобно оформлять подобные конструкции с помощью switch case, а так же узнаем немного о enum типах, которые хорошо подходят для работы со switch case.

Конструкция switch-case — это удобная замена длинной if-else конструкции, которая сравнивает переменную с несколькими константными значениями, например int или char.

Синтаксис

switch ( <переменная> ) {
case значение1:
  Выполнить если <переменная> == значение1
  break;
case значение2:
  Выполнить если <переменная> == значение2
  break;
...
default:
  выполнить, если ни один вариант не подошел
  break;
}

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

int a=1;
switch(a)
{
    case 1:
        a++;
    case 2:
        a++;
    case 3:
        a++;
}
cout<<"a= "<<a;

Данная программа выведет a = 4.

Значения для сравнения, описанные после case могут быть только константами, поэтому следующий вариант использования switch-case — неверен:

int a = 10;
int b = 10;
int c = 20;

switch ( a ) {
case b:
  // Code
  break;
case c:
  // Code
  break;
default:
  // Code
  break;
}

При попытке скомпилировать данную программу, вы получите подобное сообщение:

test.cpp:9: error: 'b' cannot appear in a constant-expression

Блок default — необязателен, но он полезен для обработки исключительных ситуации.

Следующая программа демонстрирует один из возможных вариантов использования switch-case:

#include <iostream>

using namespace std;

void playgame()
{
    cout << "Play game called";
}
void loadgame()
{
    cout << "Load game called";
}
void playmultiplayer()
{
    cout << "Play multiplayer game called";
}

int main()
{
  int input;

  cout<<"1. Play game\n";
  cout<<"2. Load game\n";
  cout<<"3. Play multiplayer\n";
  cout<<"4. Exit\n";
  cout<<"Selection: ";
  cin>> input;
  switch ( input ) {
  case 1:
    playgame();
    break;
  case 2:
    loadgame();
    break;
  case 3:
    playmultiplayer();
    break;
  case 4:
    cout<<"Thank you for playing!\n";
    break;
  default:
    cout<<"Error, bad input, quitting\n";
    break;
  }
  cin.get();
}

Эта программа показывает простой способ обработки вводимых пользователем данных.

Минус данной программы в том, что она дает только одну попытку, без права на ошибку 🙂 . Это легко исправить, заключив весь блок switch-case в цикл. Но может возникнуть вопрос: внутри switch-case есть break, не прервет ли он выполнение цикла? Нет, break прервет только выполнение блока switch.

Сравнение switch-case с if-else

Если у вас возникают проблемы с пониманием того, как работает switch-case, посмотрите на следующую if-else конструкцию, она работает точно так же, как и switch

if ( 1 == input ) 
{ 
    playgame();
} 
else if ( 2 == input )
{ 
    loadgame();
} 
else if ( 3 == input )
{ 
    playmultiplayer();
} 
else if ( 4 == input )
{ 
    cout << "Thank you for playing!\n";
} 
else
{ 
    cout << "Error, bad input, quitting\n";
} 

Если мы можем сделать то же самое с помощью if-else, зачем вообще нужен switch? Главное преимущество этой конструкции в том, что нам понятно, как работает программа: единственная переменная контролирует поведение программы. В случае с if-else, придется внимательно читать каждое условие.

Создаем собственные типы с помощью enumeration

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

Тип enum (сокращение от «enumerated type«, перечисляемые типы) содержит перечисление различных значений, например цветов радуги:

enum RainbowColor {
    RC_RED, RC_ORANGE, RC_YELLOW, RC_GREEN, RC_BLUE, RC_INDIGO, RC_VIOLET
};

Несколько важных моментов:

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

Теперь вы можете объявлять переменные с типом RainbowColor:

RainbowColor chosen_color = RC_RED;

И, как уже говорилось, эти переменные хорошо подходят для использования в switch:

switch (chosen_color)
{ 
    case RC_RED: /* paint screen red */
    case RC_ORANGE: /* paint screen orange */
    case RC_YELLOW: /* paint screen yellow */
    case RC_GREEN: /* paint screen green */
    case RC_BLUE: /* paint screen blue */
    case RC_INDIGO: /* paint screen indigo */
    case RC_VIOLET: /* paint screen violet */
    default: /* обработка исключений */
}

Так как мы используем перечисления, мы можем быть уверенными, что рассмотрели все возможные значения переменной. Значения констант в перечислении — это простой int, по умолчанию каждое следующее значение больше предыдущего на 1. Для первого — 0, для второго — 1 и т.д. В нашем случае: RC_RED = 0 и RC_ORANGE = 1.

Вы также можете задать собственные значения:

enum RainbowColor {
    RC_RED = 1, RC_ORANGE = 3, RC_YELLOW = 5, RC_GREEN = 7, RC_BLUE = 9, 
    RC_INDIGO = 11, RC_VIOLET = 13
};

Преимущество использования перечисляемых типов в том, что вы можете задать имя значениям, которые иначе пришлось бы хард-кодить. Например, если вы пишете игру крестики-нолики, вам нужен способ представления крестиков и ноликов на доске. Это может быть 0 для пустой клетки, 1 для нолика и 2 для крестика. Значит, наверняка придется использовать подобный код:

if ( board_position == 1 )
{ 
    /* do something */
}

Данный код очень сложен для понимания и обслуживания, потому что совсем не понятно, что означает цифра 1. Enum типы позволяют избегать таких ситуаций:

enum TicTacToeSquare { TTTS_BLANK, TTTS_O, TTTS_X };

if ( board_position == TTTS_O )
{ 
    /* some code */
}

Теперь все гораздо понятнее 🙂 .

На этом всё! Подписывайтесь и не пропустите новые уроки! 🙂

Хочешь получать статьи на почту?

Подпишись на обновления!
* Ваш email не будет разглашен/продан. Вы сможете отписаться в любое время.

10 Комментариев

  1. VAN says:

    А что, если на вариантах 3 и 5 должно происходить одно и то же? Не писать же это одно и то же два раза!
    Можно ли одним case обслужить несколько вариантов?

    1. root says:

      Здравствуйте!
      Используйте такую конструкцию:

      switch (i) {
      	case 2:
      		cout << 1 << endl;
      		break;
      	case 3:
      	case 5:
      		cout << "same event\n";
      		break;
      	case 6:
      		cout << 6 << endl;
      }
      
  2. Игорь says:

    Тогда пишешь:
    swith (i){
    case 3, 5 :
    cout << "example";
    break;
    }

    1. eusi says:

      Запятая не работает в этом операторе в Си в среде Dev-C++.

  3. Вадим says:

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

    #include «iostream.h»
    #include «windows.h»
    #include «string.h»
    #include «ctype.h»
    
    char* Rus(const char* text);
    void main()
    { int men;
    cout<<Rus("Програма №1: пiдрахунок кiлькостi букв А \n");
    cout<<Rus("Програма №2:Визначення першои букви другого слова\n");
    cout<>men;
    switch(men)
    {case 1:
    {
    	char stroka[50];
    	int b,l,g;
    	cout<<Rus("Задайте значення з клавiатури\n");
    	cin.getline(stroka,50);
    	b=strlen(stroka);
    	cout<<Rus("Довжина рядка\n");
    	cout<<b<<endl;
    	g=0;
    	for (l=0;l<=b;l++) if(stroka[l]=='a') ++g;
    	cout<<Rus("Кiлькiсть букв А\n");
    	cout<<g<<endl;
    	system ("pause");
    }
    break;
    
    case 2:
    	{	char strok[50];
    	int j,x,y;
    	cout<<Rus("Задайте значення з клавiатури\n");
    	cin.getline(strok,50);
    	x=strlen(strok);
    	for (j=0;j<=x;j++) if(strok[j]==' ')break;y=j;++y;
    	cout<<Rus("Перша буква другого слова\n");
    	cout<< strok[y]<<endl;
    	system ("pause");
    	}	break;
    
    case 3:
    	{char str[50];
    	int m,i,p;
    	cout<<Rus("Задайте значення з клавiатури\n");
    	cin.getline(str,50);
    	m=strlen(str);
    	cout<<Rus("Довжина рядка\n");
    	cout<<m<<endl;
    	p=0;
    	for (i=0;i<=m;i++) if(str[i]==' ') break; p=i;
    	cout<<Rus("Довжина слова\n");
    	cout<<p<<endl;
    	system ("pause");
    	}break;
    default:cout<<Rus(" Невiрний формат\n");
    	system ("pause");
    }
    
    }
    
    char bufRus[256];
    char* Rus(const char* text)
    {
    	CharToOem(text,bufRus);
    	return bufRus;
    }
    
    1. vasso Picasso says:

      Здорова.
      «coutmen;» измени на «cin>>men», ты пишешь вместо чтения.

      И далее возможны проблемы с использованием «cin.getline(stroka,50);»,
      может понадобиться «cin.ignore();» перед ним. не уверен …

      Лучше делай cin.getline(stroka, sizeof(stroka));

  4. ErrorER says:

    а строки можно после case ставить?

    1. root says:

      Здравствуйте!
      К сожалению, возможности использовать оператор switch-case со строковыми константами нет.

  5. Shiny says:

    Большое спасибо! Все вроде бы понятно. Хм интересно почему нельзя поставить переменные после case

  6. Рената says:

    Помогите, пожалуйста, что не так? Нужно с помощью swithc-case позволить делать выбор между умножением и вычитанием:

    #include "stdafx.h"
    #include 
    #include 
    #include 
    #include 
    
    #define d1 10
    #define d2 100
    #define N 10
    
    int main()
    {
    	setlocale(LC_ALL, "Russian");
    	int numb1, numb2, correctAnswer, answer, counter;
    	char reset;
    	printf("Умножение или вычитание? y/n \n");
    		fflush(stdin);
    		scanf("%c", &amp;reset); 
    		 
    		switch(reset)
    		{
    		case 'y':
    			{
    				
    				
    		
    
    	char reset = 1;
    	while(reset)
    	{
    		numb1 = 0;
    		numb2 = 0;
    		correctAnswer = 0;
    		answer = 0;
    		counter = 0;
    		
    		for(int i = 0; i &lt; N; ++i)
    		{
    			srand(time(NULL));
    			numb1 = d1 + rand() % d2;
    			numb2 = d1 + rand() % d2;
    			correctAnswer = numb1 - numb2;
    
    			printf(&quot;%d - %d = &quot;, numb1, numb2);
    			scanf(&quot;%d&quot;, &amp;answer);
    			if(answer == correctAnswer)
    			{
    				printf(&quot;Correct!\n&quot;);
    				++counter;
    			}
    else
    			{
    				printf(&quot;Incorrect! &quot;);
    				printf(&quot;%d - %d = %d\n&quot;, numb1, numb2, correctAnswer);
    			}
    		}
    
    		printf(&quot;Correct answers qty: %d\n&quot;, counter);
    		printf(&quot;Grade: &quot;);
    		break;
    	}
    		case &#039;n&#039;:
    			{
    				
    	{numb1 = 0;
    		numb2 = 0;
    		correctAnswer = 1;
    		answer = 1;
    		counter = 1;
    		
    		for(int i = 0; i &lt; N; ++i)
    		{
    			srand(time(NULL));
    			numb1 = d1 + rand() % d2;
    			numb2 = d1 + rand() % d2;
    			correctAnswer = numb1 * numb2;
    
    			printf(&quot;%d * %d = &quot;, numb1, numb2);
    			scanf(&quot;%d&quot;, &amp;answer);
    			if(answer == correctAnswer)
    			{
    				printf(&quot;Correct!\n&quot;);
    				++counter;
    			}
    else
    			{
    				printf(&quot;Incorrect! &quot;);
    				printf(&quot;%d - %d = %d\n&quot;, numb1, numb2, correctAnswer);
    			}
    		}
    
    		printf(&quot;Correct answers qty: %d\n&quot;, counter);
    		printf(&quot;Grade: &quot;);
    		break;
    	}
    		
    		{
    		case 10:
    			{
    				printf(&quot;A\n&quot;);
    				break;
    			}
    		case 9:
    		case 8:
    			{
    				printf(&quot;B\n&quot;);
    				break;
    			}
    		case 7:
    		case 6:
    			{
    				printf(&quot;C\n&quot;);
    				break;
    			}
    		default:
    			printf(&quot;D\n&quot;);
    		}
    
    ex0: 
    printf(&quot;Would you like to repeat? y/n \n&quot;);
    		fflush(stdin);
    		scanf(&quot;%c&quot;, &amp;reset); 
    		 
    		switch(reset)
    		{
    		case &#039;y&#039;:
    			{
    				break;
    			}
    		case &#039;n&#039;:
    			{
    				return 0;
    				break;
    			}
    		default:
    			goto ex0;
    		}
    	}
    	
    	return 0;
    }
    

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *