在使用if,else if,else if,…和使用switch(){case A:… case B:…}之间C和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。

存在差异 – 对于交换机,编译器可以优化交换机以使用查找表。 如果有许多值彼此足够接近,则这是可能的。 例如,这个开关:

 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 

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

根据标准,有几个不同之处。

  1. 可以在if语句链中多次评估该value ,一次在switch语句中。 如果评估value没有副作用,这是不重要的。
  2. if链不会允许通过,而switch语句将没有break
  3. if链允许一般比较,但switch语句只允许比较常量积分表达式。
  4. 使用break; 是不同的。 它突破了switch语句,但更进一步。 如果您需要根据条件中断循环或封闭switch语句,则需要if链。
  5. 由于switch语句实际上是对case语句或default:它可以在不同的地方工作,典型的例子是Duff的设备 。 (IIRC,Tom Duff认为这是一个强有力的争论,但他不知道在哪一方。)

因此,如果评估值没有副作用,请break; 语句一致地使用,并且仅在switch ,比较是恒定的积分值,并且它没有以时髦的方式使用,行为可以是相同的。 是否有任何编译器将使用此等效性是另一个问题。

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

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

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

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

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

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

源代码:

AC

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

公元前

 #include  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  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 ac -oa;gcc bc -ob;gcc cc -oc 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 ac -oa -O2;gcc bc -ob -O2;gcc cc -oc -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的程序(ac和cc)比switch更快(大约10倍!)。

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

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

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

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

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

但有一点很重要,那就是case标签必须是在编译时评估的常量表达式。 if比较的两边(如果你将if语句减少到那个)在运行时进行评估。