当我发现我只能在 C++ 的switch
语句中使用数值时,我认为它与一堆if-else
's 之间一定有更深层次的区别。
因此我问自己:
- (如何)在运行时速度、编译时优化和一般编译方面有什么
switch
不同?if-elseif-elseif
我这里主要说的是MSVC。
当我发现我只能在 C++ 的switch
语句中使用数值时,我认为它与一堆if-else
's 之间一定有更深层次的区别。
因此我问自己:
switch
不同?if-elseif-elseif
我这里主要说的是MSVC。一个开关经常被编译成一个跳转表(一个比较来找出要运行的代码),或者如果这不可能,编译器仍然可以重新排序比较,以便在值之间执行二进制搜索(log N比较)。if-else 链是线性搜索(尽管我想,如果所有相关值都是编译时整数常量,编译器原则上可以执行类似的优化)。
Switch 语句通常是编译器优化的常见来源。也就是说,如何处理它们取决于您在编译器上使用的优化设置。
编译 switch 语句的最基本(未优化)方法是将其视为if ... else if ...
语句链。编译器优化开关的常用方法是将其转换为如下所示的跳转表:
if (condition1) goto label1;
if (condition2) goto label2;
if (condition3) goto label3;
else goto default;
label1:
<<<code from first `case statement`>>>
goto end;
label2:
<<<code from first `case statement`>>>
goto end;
label3:
<<<code from first `case statement`>>>
goto end;
default:
<<<code from `default` case>>>
goto end;
end:
这种方法更快的一个原因是条件句中的代码更小(因此,如果条件被错误预测,指令缓存惩罚会更小)。此外,“失败”的情况变得更容易实现(编译器放弃了该goto end
语句)。
编译器可以通过创建一个指针数组(指向由标签标记的位置)来进一步优化跳转表,并将您打开的值用作该数组的索引。这将消除代码中几乎所有的条件(除了验证您打开的值是否与您的案例之一匹配所需的任何条件)。
提醒一句:嵌套跳转表很难生成,一些编译器甚至拒绝尝试创建一个。出于这个原因,如果最大限度优化的代码对您很重要,请避免将 a 嵌套switch
在另一个内部switch
(我不是 100% 确定 MSVC 特别是如何处理嵌套switch
的 es,但编译器手册应该告诉您)。