Подтвердить что ты не робот

О вызове switch {} в C?

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

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

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

4b9b3361

Ответ 1

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

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

В 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;
}

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

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

Ответ 2

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

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

Ответ 3

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

Удивительно, но многие программисты никогда не задают этот вопрос, но рассматривают 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-else  switch
Symbols    33       65        // Not counting the expressions
Lines      12       19        // Not counting empty lines
Переключатель

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

Прежде чем вы начнете рассуждать о том, что коммутатор выглядит скорее как таблица, чем 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 следует использовать для удобочитаемости, но я предпочитаю оставлять такие обсуждения вне темы для сайтов, не связанных с программированием.


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

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

typedef void (*Func_t)(void);

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

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

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

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

Ответ 4

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

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") /* ... */;)

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

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

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

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

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

Ответ 8

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

В качестве хорошей практики float/doubles не должны проверяться на равенство, "if (f = 3.141516)" - это приглашение для головных болей, "const float kEpsilon = 1e-5;" и затем используйте "if (fabs (f - 3.141516) < kEpsilon)" Выберите значение epsilon, соответствующее вашей проблеме. Встроенная функция или макрос могут помочь записать это более читаемым способом.

Ответ 9

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