Оператор switch — различия между версиями

Материал из Вики ИТ мехмата ЮФУ
Перейти к: навигация, поиск
(добавлена цепочка if (если необходимы диапазоны), улучшено форматирование)
 
(не показаны 4 промежуточные версии этого же участника)
Строка 1: Строка 1:
 
Оператор выбора switch в C/C++ похож на оператор выбора case в Паскале. Например, выбор можно осуществлять по выражению «интегрального» типа (целые типы, символьный тип). Однако имеется, по меньшей мере, два важных отличия. Первое состоит в том, что в качестве меток используются только константы или константные выражения. То есть не могут, например, использоваться диапазоны, как в Паскале, нельзя также перечислять значения через запятую. Второе отличие — более тонкое, поясним его на примере.
 
Оператор выбора switch в C/C++ похож на оператор выбора case в Паскале. Например, выбор можно осуществлять по выражению «интегрального» типа (целые типы, символьный тип). Однако имеется, по меньшей мере, два важных отличия. Первое состоит в том, что в качестве меток используются только константы или константные выражения. То есть не могут, например, использоваться диапазоны, как в Паскале, нельзя также перечислять значения через запятую. Второе отличие — более тонкое, поясним его на примере.
 
<source lang="Pascal">case i of
 
<source lang="Pascal">case i of
   1 : f;
+
   1: f;
   2 : g;
+
   2: g;
   else : h;
+
   else: h;
 
end; // case
 
end; // case
 
</source>
 
</source>
 
Если попытаться дословно перевести его на C, получится:
 
Если попытаться дословно перевести его на C, получится:
 
<source lang="cpp">switch (i) {
 
<source lang="cpp">switch (i) {
   case 1 : f();
+
   case 1: f();
   case 2 : g();
+
   case 2: g();
   default : h();
+
   default: h();
}
+
} // switch
 
</source>
 
</source>
И это будет неверным. Потому что в случае i = 1 будут вызваны все три функции f, g, h. Дело в том, что switch ищет первую константу, значение которой совпадает с i и просто передаёт управление в это место (к вызову f в случае i = 1). Далее весь код исполняется подряд. Если мы хотим добиться поведения, аналогичного Паскалю, необходимо использовать оператор break для принудительного выхода из тела switch. Правильный перевод на C исходного примера на Паскаль такое:
+
И это будет '''неверным'''. Например, в случае i = 1 будут вызваны все три функции f, g, h. Дело в том, что switch ищет первую константу, значение которой совпадает с i и просто передаёт управление в это место (к вызову f в случае i = 1). Далее весь код исполняется подряд. Если мы хотим добиться поведения, аналогичного Паскалю, необходимо использовать оператор break для принудительного выхода из тела switch.  
 +
 
 +
'''Правильный перевод на C''' исходного примера на Паскаль такой:
 
<source lang="cpp">switch (i) {
 
<source lang="cpp">switch (i) {
   case 1 : f(); break;
+
   case 1:
   case 2 : g(); break;
+
    f();
   default : h();
+
    break;
 +
   case 2:
 +
    g();
 +
    break;
 +
   default:
 +
    h();
 
}
 
}
 
</source>
 
</source>
 +
Стоит напомнить, что если в ветке case содержится оператор возврата из функции <tt>return</tt>, то ставить в ней <tt>break</tt> '''излишне'''.
  
 
+
Используя описанную выше особенность поведения <tt>switch</tt>, можно смоделировать перечисление констант, как в Паскале:
Используя описанную особенность поведения switch, можно смоделировать перечисление констант, как в Паскале:
 
 
<source lang="Pascal">case i of
 
<source lang="Pascal">case i of
   1 : f;
+
   1: f;
   2, 3 : g;
+
   2, 3: g;
   else : h;
+
   else h;
 
end; // case
 
end; // case
 
</source>
 
</source>
 
эквивалентно:
 
эквивалентно:
 
<source lang="cpp">switch (i) {
 
<source lang="cpp">switch (i) {
   case 1 : f(); break;
+
   case 1:
   case 2 : case 3 : g(); break;
+
    f();
   default : h();
+
    break;
 +
   case 2: case 3:
 +
    g();
 +
    break;
 +
   default:
 +
    h();
 
}
 
}
 
</source>
 
</source>
 +
 +
Однако в C и C++ нет аналога выражений-диапазонов из Паскаля (например, <tt>6..15</tt>). Если требуется такая проверка принадлежности диапазону, то придётся писать десять <tt>case</tt> или заменить <tt>switch</tt> на обычную цепочку условных операторов:
 +
<source lang="cpp">if (1 == i) {
 +
    f();
 +
} else if (2 == i || 3 == i) {
 +
    g();
 +
} else if (6 <= i && i <= 15)
 +
    h();
 +
}
 +
</source>
 +
В этом примере стоит обратить внимание на:
 +
 +
# Оформление конструкции <tt>else if</tt> (без перехода на новую строку и без дополнительных отступов), в этом случае её вид достаточно хорошо отражает смысл происходящего.
 +
# Сравнение на равенство: <tt>3 == i</tt> вместо более привычного <tt>i == 3</tt> — так часто пишут, чтобы исключить возможность ошибки, когда вместо <tt>==</tt> используется <tt>=</tt>.
 +
# Оформление двойного неравенства: в таком виде оно больше всего похоже на математическую запись.
  
 
[[Категория:C++]]
 
[[Категория:C++]]

Текущая версия на 11:22, 18 сентября 2014

Оператор выбора switch в C/C++ похож на оператор выбора case в Паскале. Например, выбор можно осуществлять по выражению «интегрального» типа (целые типы, символьный тип). Однако имеется, по меньшей мере, два важных отличия. Первое состоит в том, что в качестве меток используются только константы или константные выражения. То есть не могут, например, использоваться диапазоны, как в Паскале, нельзя также перечислять значения через запятую. Второе отличие — более тонкое, поясним его на примере.

case i of
  1: f;
  2: g;
  else: h;
end; // case

Если попытаться дословно перевести его на C, получится:

switch (i) {
  case 1: f();
  case 2: g();
  default: h();
} // switch

И это будет неверным. Например, в случае i = 1 будут вызваны все три функции f, g, h. Дело в том, что switch ищет первую константу, значение которой совпадает с i и просто передаёт управление в это место (к вызову f в случае i = 1). Далее весь код исполняется подряд. Если мы хотим добиться поведения, аналогичного Паскалю, необходимо использовать оператор break для принудительного выхода из тела switch.

Правильный перевод на C исходного примера на Паскаль такой:

switch (i) {
  case 1:
    f();
    break;
  case 2:
    g();
    break;
  default:
    h();
}

Стоит напомнить, что если в ветке case содержится оператор возврата из функции return, то ставить в ней break излишне.

Используя описанную выше особенность поведения switch, можно смоделировать перечисление констант, как в Паскале:

case i of
  1: f;
  2, 3: g;
  else  h;
end; // case

эквивалентно:

switch (i) {
  case 1:
    f();
    break;
  case 2: case 3:
    g();
    break;
  default:
    h();
}

Однако в C и C++ нет аналога выражений-диапазонов из Паскаля (например, 6..15). Если требуется такая проверка принадлежности диапазону, то придётся писать десять case или заменить switch на обычную цепочку условных операторов:

if (1 == i) {
    f();
} else if (2 == i || 3 == i) {
    g();
} else if (6 <= i && i <= 15)
    h();
}

В этом примере стоит обратить внимание на:

  1. Оформление конструкции else if (без перехода на новую строку и без дополнительных отступов), в этом случае её вид достаточно хорошо отражает смысл происходящего.
  2. Сравнение на равенство: 3 == i вместо более привычного i == 3 — так часто пишут, чтобы исключить возможность ошибки, когда вместо == используется =.
  3. Оформление двойного неравенства: в таком виде оно больше всего похоже на математическую запись.