在使用if,else if,else if,...和使用switch(){case A:... case B:...}之间C和C ++是否有任何区别?

时间:2010-07-29 17:27:19

标签: c

我感兴趣的是,如果我使用C / C ++编译器的角度有什么不同:

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

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

我觉得没有区别,只有句法。有谁知道更多呢?

谢谢,Boda Cydo。

9 个答案:

答案 0 :(得分:8)

是的,有差异。级联if保证按顺序评估条件。该开关仅保证对用作开关参数的任何内容进行单一评估。根据编译器的不同,无论选择的分支如何,交换机通常都会花费(几乎)恒定的时间,而if级联几乎可以保证第一条腿最快,第二条腿最快,依此类推最后一个是最慢的。

答案 1 :(得分:6)

存在差异 - 对于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

答案 2 :(得分:3)

根据标准,存在一些差异。

  1. value语句可以在if链中多次评估switch。如果评估value没有副作用,这就不重要了。
  2. if链不会允许通过,而switch语句将在没有break的情况下发生。
  3. if链允许进行一般比较,但switch语句只允许比较常量积分表达式。​​
  4. 使用break;是不同的。它突破了switch声明,但更进一步。如果您需要根据条件中断循环或括起switch语句,则需要if链。
  5. 由于switch语句实际上是gotocase语句或default:,因此它可以在不同的地方使用,典型的例子是Duff's device 。 (IIRC,汤姆达夫认为这是一个强有力的争论,但他不确定在哪一方。)
  6. 因此,如果评估value没有副作用,break;语句将一致地使用,并且仅在switch中使用,则比较是常量积分值,并且它不用于时髦的方式,行为可以是相同的。是否有任何编译器将使用此等效性是另一个问题。

答案 3 :(得分:1)

这取决于编译器如何选择优化代码。编译器的代码优化是一个巨大的领域。

要找到编译器的确切答案,请确定如何使用它构建汇编代码并查看写入文件的不同汇编代码。

这已经使用一个编译器完成,您可以在此处查看结果。 http://www.eventhelix.com/RealtimeMantra/Basics/CToAssemblyTranslation3.htm

但简短的答案是肯定的。他们很可能会有所不同。

答案 4 :(得分:1)

我遇到了同样的问题,所以我做了一些测试,这里是使用gcc版本3.4.6 / centos 4获得的一些结果

a.c和c.c使用ifs,但是c.c从命令行获取变量,因此编译器在编译时不知道“b”的值。 b.c使用开关

源代码:

交流转换器

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

b.c

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

C.C

#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

令人惊讶的是,使用ifs的程序(a.c和c.c)都比切换更快(大约10倍!)。

答案 5 :(得分:0)

应该使用间接寻址将开关编译为跳转,而if语句的序列将是条件跳转链。第一个是恒定时间;当然,你可以在if中添加更多一般条件。

编辑:我应该提一下,如果一些智能编译器能够检测到ifs链中的所有条件都具有特定的简单形式并转换为开关,我不会感到惊讶。我不知道他们是否这样做,但你总是可以反编译并检查。

答案 6 :(得分:0)

case语句可能被编译成一个“跳转表”,如果有几十个案例并且你执行了数百万次这可能会更快。

答案 7 :(得分:0)

如果select值是一个整数(它必须是C / C ++),那么编译器可以用跳转表替换if。

答案 8 :(得分:0)

+1 David Thomley的答案,因为我觉得这是最完整的。

但缺少一个重要的事情,那就是case标签必须是在编译时评估的常量表达式。使用if比较的两端(如果将if语句减少到该值)将在运行时进行评估。