我感兴趣的是,如果我使用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。
答案 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)
根据标准,存在一些差异。
value
语句可以在if
链中多次评估switch
。如果评估value
没有副作用,这就不重要了。if
链不会允许通过,而switch
语句将在没有break
的情况下发生。if
链允许进行一般比较,但switch
语句只允许比较常量积分表达式。break;
是不同的。它突破了switch
声明,但更进一步。如果您需要根据条件中断循环或括起switch
语句,则需要if
链。switch
语句实际上是goto
到case
语句或default:
,因此它可以在不同的地方使用,典型的例子是Duff's device 。 (IIRC,汤姆达夫认为这是一个强有力的争论,但他不确定在哪一方。)因此,如果评估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
语句减少到该值)将在运行时进行评估。