Есть ли разница в C и C ++ между использованием if, else if, else if, ... и использованием switch () {case A: ... case B: ...}? - PullRequest
6 голосов
/ 29 июля 2010

Мне интересно, есть ли какая-либо разница с точки зрения компилятора C или C ++, использую ли я:

if (value == a) {
    ...
}
else if (value == b) { 
    ...
}
else if (value == c) { 
    ...
}

против

switch (value) {
    case a:
        ...
        break;
    case b:
        ...
        break;
    case c:
        ...
        break;
}

Мне кажется, что нет никакой разницы, только синтаксический. Кто-нибудь знает об этом больше?

Спасибо, Бода Цидо.

Ответы [ 9 ]

8 голосов
/ 29 июля 2010

Да, есть различия.Каскадные if s гарантируют оценку условий по порядку.Переключатель гарантирует только одну оценку того, что используется в качестве параметра переключателя.В зависимости от компилятора, коммутатор часто занимает (почти) постоянное время независимо от выбранной ветви, тогда как каскад if в значительной степени гарантирует, что первый этап самый быстрый, второй - самый быстрый, и так далее до последнегомедленнее.

6 голосов
/ 29 июля 2010

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

switch ( integer ) {
  case 10:
     xxx
     break;
  case 12:
     yyy
     break;
  case 13
     zzz
     break;
}

может стать (псевдокод):

address = lookup[ integer - 10 ]; // which is prefilled with { case_10, err, err, case_12, case 13 }
goto address;
case_10: xxx; goto err;
case_12: yyy; goto err;
case_13: zzz; 
err: //do nothing
3 голосов
/ 29 июля 2010

В соответствии со Стандартом есть несколько отличий.

  1. value может оцениваться несколько раз в цепочке if, один раз в операторе switch.Если оценка value не имеет побочных эффектов, это неважно.
  2. Цепочка if не допустит падения, тогда как оператор switch будет иметь падение без break.
  3. Цепочка if допускает общее сравнение, но оператор switch позволяет только сравнение с константными интегральными выражениями.
  4. Использование break; отличается.Он выходит из оператора switch, но дальше.Если вам нужно выйти из цикла или заключить оператор switch в зависимости от условия, вам нужна цепочка if.
  5. Поскольку оператор switch, по существу, выполняет goto до case оператор или default:, он будет работать в разных местах, наиболее типичным примером является устройство Даффа .(IIRC, Том Дафф счел это сильным аргументом в вопросе о прорыве, но он не знал, с какой стороны.)

Итак, если value оценивается без побочных эффектов, break; операторы используются последовательно и только в switch, сравнение проводится с постоянными целочисленными значениями, и оно не используется в стиле фанк, поведение может быть идентичным.Будет ли какой-либо компилятор использовать эту эквивалентность - другой вопрос.

1 голос
/ 12 сентября 2012

Я столкнулся с той же проблемой, поэтому провел несколько тестов, вот некоторые результаты, полученные с использованием gcc версии 3.4.6 / centos 4

ac и cc, использующих ifs, но cc берет переменную из командной строкипоэтому компилятор не знает значение "b" во время компиляции.bc использует switch

исходные коды:

ac

#include <stdint.h>
int main(){
uint32_t  i,b=10,c;
    for(i=0;i<1000000000;i++){
        if(b==1) c=1;
        if(b==2) c=1;
        if(b==3) c=1;
        if(b==4) c=1;
        if(b==5) c=1;
        if(b==6) c=1;
        if(b==7) c=1;
    }
}

bc

#include <stdint.h>
int main(){
uint32_t  i,b=10,c;
    for(i=0;i<1000000000;i++){
        switch(b){
        case 1:
                c=1;
                break;
        case 2:
                c=1;
                break;
        case 3:
                c=1;
                break;
        case 4:
                c=1;
                break;
        case 5:
                c=1;
                break;
        case 6:
                c=1;
                break;
        case 7:
                c=1;
                break;
        }
    }
}

cc

#include <stdint.h>
int main(int argc, char **argv){
uint32_t  i,b=10,c;

    b=atoi(argv[1]);
    for(i=0;i<1000000000;i++){
        if(b==1) c=1;
        if(b==2) c=1;
        if(b==3) c=1;
        if(b==4) c=1;
        if(b==5) c=1;
        if(b==6) c=1;
        if(b==7) c=1;
    }
}

сначала мыскомпилируйте программы без флагов оптимизации:

root@dev2 ~ # gcc a.c -o a;gcc b.c -o b;gcc c.c -o c
root@dev2 ~ # time ./a

real    0m4.871s
user    0m4.866s
sys     0m0.005s
root@dev2 ~ # time ./b

real    0m1.904s
user    0m1.904s
sys     0m0.000s
root@dev2 ~ # time ./c 10

real    0m4.848s
user    0m4.836s
sys     0m0.009s

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

Теперь мы компилируем, используя -O2:

root@dev2 ~ # gcc a.c -o a -O2;gcc b.c -o b -O2;gcc c.c -o c -O2
root@dev2 ~ # time ./a

real    0m0.055s
user    0m0.055s
sys     0m0.000s
root@dev2 ~ # time ./b

real    0m0.537s
user    0m0.535s
sys     0m0.001s
root@dev2 ~ # time ./c 10

real    0m0.056s
user    0m0.055s
sys     0m0.000s

Удивительно, но обе программы (ac и cc), использующие if, работают быстрее (примерно в 10 раз!), Чем switch.

1 голос
/ 29 июля 2010

Это будет зависеть от того, как компилятор решит оптимизировать ваш код. Оптимизация кода для компилятора - это огромное поле.

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

Это уже было сделано с одним компилятором, и вы можете увидеть результаты здесь. http://www.eventhelix.com/RealtimeMantra/Basics/CToAssemblyTranslation3.htm

Но короткие короткие ответы - да. скорее всего, они будут другими.

0 голосов
/ 30 июля 2010

+ 1 для ответа Дэвида Томли, поскольку я действительно считаю его наиболее полным.

Однако упускается одна важная вещь: метки case должны быть константными выражениями, которые оцениваются во время компиляции.С if обе стороны сравнения (если вы уменьшите оператор if до этого) оцениваются во время выполнения.

0 голосов
/ 29 июля 2010

Возможно, если значение select является целым числом (которое должно быть в C / C ++), что компилятор может заменить if на таблицу переходов.

0 голосов
/ 29 июля 2010

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

0 голосов
/ 29 июля 2010

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

Edit: я должен упомянуть, что я не удивлюсь, если некоторые умные компиляторы смогут обнаружить, что все условия в цепочке if имеют определенную простую форму и преобразовать в переключатель Я не знаю, так ли это, но вы всегда можете декомпилировать и проверить.

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