LINUX.ORG.RU

switch и последовательность нескольких case

 , ,


0

1

Я не совсем понимаю. Насколько плохо делать вот так.

switch(ch)
{
    case '%': case '$': case '#': foo(ch); break;
    case '!': bar(ch); break;
    default: break;
}

Вместо этого

switch(ch)
{
    case '%': foo(ch); break;
    case '$': foo(ch); break;
    case '#': foo(ch); break;
    case '!': bar(ch); break;
    default: break;
}

Или первое разворачивается во второе и всё хорошо? Тут коротко для примера, но допустим таких case 1: case 50 case 500: ... bla(data) штук 100500.

@Harald @i-rinat @beastie

UDP: Разницы нет, ассемблерный вывод одинаковый gcc/clang. Но я так и не нашёл где явно описано.

UDP2: Разница есть, но мужно для каждого конкретного случая смотреть чего да как объяснения ниже.

★★★★★

Последнее исправление: LINUX-ORG-RU (всего исправлений: 3)

Это эквивалентный код. Так что хорошо бы расписать, что подразумевается под «насколько плохо».

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

Да, посмотрел gcc/clang -S. Всё абсолютно одинаково. Я просто подумал что такой switch может развернутся не в таблицу переходов, а в if/else какой. Не можно вроде как заставить так развернутся свич, короче думал что на вид хоть эквивалент, но на деле нет. Вопрос больше в том что это в стандарте прописано точно или это на усмотрение компилятора во что такое превращать. Эту информацию я не нашёл.

LINUX-ORG-RU ★★★★★
() автор топика
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)

пиши первым вариантом

только не в одну строчку, а каждый case на своей, для читабельности

Harald ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Из шапки не сработало, но тут да. Код эквивалентный, насколько я ещё помню сишку. Отталкивайся от читабельности. Отталкиваясь от данного примера, я бы предпочёл:

switch(ch)
{
    case '%': /* FALLTHROUTH */
    case '$': /* FALLTHROUTH */
    case '#':
        foo(ch);
        break;
    case '!':
        bar(ch);
        break;
}
beastie ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Это может развернуться в любой эквивалентный код, который соответствует текущей (не-)оптимизации: таблица, деление пополам, что-то еще, или хитрая комбинация из предыдущих.

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

Ну, да, где оно уместно и не длинно так и буду. Спасибо

LINUX-ORG-RU ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Что же, буду иметь в виду и иногда проверять, если не забуду =)

LINUX-ORG-RU ★★★★★
() автор топика

оба варианта функционально эквивалентны, но второй будет преобразован в первый при оптимизации, поскольку кода меньше.

первый вариант предпочтительней из-за читабельности(надо только отформатировать нормально).

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

но поскольку конструкции вида

switch (x) {
case 0: .... break;
case 10000: .... break;
default: break;
}

при табличной реализации даст таблицу в 10000 переходов, где заполнены только два, то для экономии памяти компилятор будет генерить код в виде двух if:

if(x == 0) {...}
else if (x == 10000) {...}
else {}

могут быть смешаные случаи типа

switch (x) {
case 0: .... break;
case 1: .... break;
case 2: .... break;

case 10000: .... break;
case 10001: .... break;
case 10002: .... break;
default: break;
}

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

if(x<10000) {
  switch (x) {
  case 0: .... break;
  case 1: .... break;
  case 2: .... break;
  }  
else {
  switch(x){
  case 10000: .... break;
  case 10001: .... break;
  case 10002: .... break;
  }
}

также короткий(мало вариантов) switch типа

  switch(x){
  case 10000: .... break;
  case 10001: .... break;
  }

компилятор скорее заменит на if, поскольку if будет выполняться быстрее тут.

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

И такой код можно делать читабельным. Место на диске дешёвое. Лишний перевод строки не помешает.

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

Во, спасибо. Тогда есть смысл наверное ещё до switch сделать табличку где translate_table[raw_value] = index++;... и в switch уже отдавать так switch(translate_table[raw_value]) тогда в switch будет уже попадать ровная последовательность без скачков и по идее ничто не должно мешать уже генерировать чисто таблицу переходов без всяких там этих вот.

LINUX-ORG-RU ★★★★★
() автор топика
Ответ на: комментарий от LINUX-ORG-RU

«Преждевременная оптимизация - корень…»

При этом эту оптимизацию современные компиляторы сделают на порядки эффективнее, чем неопытный программист. Лучше пиши саму программу.

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

В таком случае можно вместо switch прописать таблицу самому. Отдав switch только обработку состояний

table[STATES][SIGNALS]=
{
   [STATE_1][SIGNAL_1]= STATE_2,
   [STATE_1][SIGNAL_2]= STATE_ERROR,
   [STATE_2][SIGNAL_2]= STATE_3,
   [STATE_2][SIGNAL_1]= STATE_ERROR,

}

while((signal=get_data())
{
   prev_state = state
   state = table[state][signal];
   switch(state)
   {
      case STATE_2: do_state_2(); break;
      case STATE_3: do_state_3(); break;
      case STATE_ERROR: do_error(prev_state,state,signal); break;
   }
}

Только вот если сигналов и/или состояний детяток то table будет огромная задолбаешься её описывать.

LINUX-ORG-RU ★★★★★
() автор топика
Ответ на: комментарий от LINUX-ORG-RU

псевдокод простого табличного(самый эффективный) switch выглядит так

if(x < min_case) goto __default_branch;
if(x > max_case) goto __default_branch;
goto @__jumps_table[x-min_case]; ///тут берется из таблицы адрес нужного бренча и туда осуществяется переход.

то есть для всех вариантов скорость одинакова.

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

:D Я не из расчёта оптимизировать, я типа что-бы не мешать оптимизировать.

LINUX-ORG-RU ★★★★★
() автор топика
Ответ на: комментарий от LINUX-ORG-RU

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

i-rinat ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Ясно, спасибо. Ну значит если свич не на 1500 строк можно не парится. Само разрулит =)

проблема табличного свича - в размере таблицы, то есть разнице между case_max и case_min значениями.

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

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

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

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