О случае switch {} в C? - PullRequest
       1

О случае switch {} в C?

11 голосов
/ 03 марта 2011

Я читаю текст в C language.В тексте говорится, что switch{} case может принимать только целочисленный тип.

Мне просто интересно, почему switch{} case не принимает другие типы, такие как float или string.Есть ли причины этого?

Большое спасибо.

Ответы [ 9 ]

18 голосов
/ 03 марта 2011

Классическая причина, вероятно, заключается в том, что для целочисленных "выражений решений" можно проводить очень хорошие оптимизации.

По сути, вы можете отобразить список операторов case в таблицу, содержащую адреса, а затем напрямую перейти на основе значения. Очевидно, что для поплавков и строк это не работает.

В GCC вы можете сделать это вручную, используя некоторые расширения , например, так:

const char * digit_name(int d)
{
  const void * handlers[] = { &&zero, &&one, &&two, &&three, &&four,
                              &&five, &&six, &&seven, &&eight, &&nine };
  goto *handlers[d]; /* Assumes d is in range 0..9. */

zero:  return "zero";
one:   return "one";
two:   return "two";
three: return "three";
four:  return "four";
five:  return "five";
six:   return "six";
seven: return "seven";
eight: return "eight";
nine:  return "nine";
 return NULL;
}

Это обычно называется "вычисленным переходом", и должно быть понятно, как switch может быть скомпилирован в нечто очень похожее. Помогает точное определение включенного выражения, например, использование enum.

Кроме того, в C не слишком много понятия о строках на уровне языка.

3 голосов
/ 03 марта 2011

Я бы ответил вопросом: почему вы используете оператор switch, а не если ... еще если?

Удивительно, но многие программисты никогда не задают этот вопрос, но относятся к switch для чего-то фундаментального, что должно быть в языке.Это неправда!Вы можете написать любую программу на C, даже не используя switch.Строго говоря, switch является избыточной функцией.

Так зачем ее использовать?

Читаемость не причина, почему.switch на самом деле имеет гораздо худший и менее интуитивный синтаксис, чем if-else.Необходимость операторов break внутри коммутатора, странных синтаксических правил коммутатора, позволяющих объявлять регистр внутри локальной области другого регистра, произвольного расположения по умолчанию.

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

Еще один более очевидный аргумент против того, чтобы switch был более читабельным, - это «голый» код:

if (A)
{
}

else if (B)
{
}

else if (C)
{
}

else
{
}


switch(something)
{
  case A:
  {
    break;
  }

  case B:
  {
    break;
  }

  case C:
  {
    break;
  }

  default:
  {
    break;  
  }
}

If и переключатель выше эквивалентны по синтаксису и функции и должны быть скомпилированы в тот же машинный код.Вот статистика из этого примера.

         if-else  switch
Symbols    33       65        // Not counting the expressions
Lines      12       19        // Not counting empty lines

требует больше кода для того же результата, поэтому должно считаться менее читаемым, чем if-else.

Прежде чем вы начнете спорить о переключателе, который больше похож на таблицу, чем если-либо, это все о форматировании кода и не имеет значения.Ничто в стандарте не мешает вам писать такой код:

if      (A) {}
else if (B) {}
else if (C) {}
else        {}

switch(something)
{
  case A:   { break; }
  case B:   { break; }
  case C:   { break; }
  default:  { break; }
}

Я бы посчитал любую форму читабельной, если вы хотите какой-то минимальный табличный синтаксис.

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


Так что переключение менее безопаснои менее читабельный, чем если-еще.То, что затем остается привлекательным для программиста, - это эффективность.Программист с n кейсами, которые должны быть проверены программой, наверняка хотел бы иметь что-то, что делает поиск правильного кейса максимально быстрым.Это плохая идея, чтобы рассматривать все случаи в линейной форме.

Как вы, возможно, знаете, можно оптимизировать if-else или довольно много переключать, реализуя его в виде массива указателей функций:

typedef void (*Func_t)(void);

const Func_t cases [N] = { ... };
cases[i]();

Эта радикальная оптимизация частоименно то, что делает компилятор, когда сталкивается с оператором switch.Однако эта оптимизация может быть выполнена, только если все случаи являются смежными целыми числами.Если они не являются смежными, то смежность может быть создана с помощью таблицы поиска const.

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

Так что, по крайней мере, в моей книге, switch существует только для этой цели: это облегчает компиляторусоздать более эффективный код, чем if-else.Таким образом, он попадает в ту же категорию, что и встроенные ключевые слова, регистр и т. Д., Что также облегчает компилятору оптимизацию вашего кода.

3 голосов
/ 03 марта 2011

Философия языка C - это то, что вы видите, это то, что вы получаете. Здесь нет скрытых механизмов. На самом деле это одна из сильных сторон языка.

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

1 голос
/ 03 марта 2011

значения с плавающей запятой обычно не сравнимы напрямую

x = 1 / 3.0;
switch (x) {
  case 0.3333: /* ... */; break;
  case 0.333333333875634875634: /* ... */; break;
  case 0.333333333784532452321: /* ... */; break;
  case 0.333333333847632874632: /* ... */; break;
  default: break;
}

То же самое со строками (нет strcpy(buff, "foobar"); if (buff == "foobar") /* ... */;)

0 голосов
/ 08 февраля 2018

Мы не можем использовать float в случае переключателя.Это потому что поплавки неточны.Вы никогда не знаете, каким будет это число на самом деле.

0 голосов
/ 04 марта 2011

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

В соответствии с хорошей практикой float / double не следует проверять на равенство, "if (f = 3.141516)" является приглашением для головной боли, "const float kEpsilon = 1e-5;" и затем используйте "if (fabs (f - 3.141516)

0 голосов
/ 03 марта 2011

Вы должны подумать о том, «как этот код C может быть преобразован в сборку?».

Переключатель conditionnal - это просто какая-то хитрая инструкция JMP, которая, между прочим, требует сортировки случаев перед компиляцией (я полагаю, компилятор отсортирует ваши случаи), но я не уверен.

В php, например, вы можете переключать {} со строкой, которая может использовать некоторый вид дихотомического поиска (сначала он ищет первые символы и т. Д.), Как карты.

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

С таким языком, как C или C ++, вы быстро создадите программу, и ваш компилятор сможет выполнять тривиальные оптимизации, но, ради бога, будьте осторожны при использовании языка программирования, подумайте об общей картине вашей программы раньше и не забывайте, что языки - это всего лишь инструменты, они не магические: если вы забыли об базовом поведении на низком уровне, вы облажались.

0 голосов
/ 03 марта 2011

Короткий ответ: целочисленные типы легко сравнивать, и сравнение выполняется очень быстро.Типы с плавающей точкой, как они есть в C, не могут быть надежно сопоставлены.Я не думаю, что C имеет строковые типы, но строки сравниваются медленно ... вам придется сравнивать массивы символов ... медленно.Я уверен, что кто-то даст вам более полный и более научный ответ.

0 голосов
/ 03 марта 2011

Что ж, сравнение значений с плавающей точкой не является надежным из-за ошибок округления, и сравнение строк по умолчанию не поддерживается C (например, только функцией strcmp)

-> нет способа автоматически определитьметоды сравнения компилятором.

...