为什么要使用switch语句而不是if-else语句?

63

我一直对这个问题很好奇。虽然我不是一个狂热的程序员,主要是写一些小的Python脚本和几个分子动力学模拟。那么真正的问题是: switch语句有什么用?为什么不能只使用if-else语句

谢谢你的回答,如果之前已经有人问过这个问题,请给我提供链接。

编辑

S.Lott指出这可能是重复的问题If/Else vs. Switch。如果您想关闭它,请这样做,我会将其保留以供进一步讨论。


这里曾经有一个非常类似的问题被问到过(https://dev59.com/73RB5IYBdhLWcg3w-8Ho)。 - Perpetualcoder
1
重复的问题:https://dev59.com/OXVD5IYBdhLWcg3wE3Xz,https://dev59.com/uHRC5IYBdhLWcg3wJNmf - S.Lott
几个分子动力学模拟。我猜你没有在其中使用任何的switch语句 :P 有趣。 - OscarRyz
1
奥斯卡,实际上我没有。模拟可能很复杂,但决策并不复杂,主要是数学。 - Nope
将其标记为 [tag:language-agnostic]。需要考虑特定语言的限制和要求。虽然问题是关于Python的,但回答包括C、C#、Java、Javascript、VB等其他语言。 - smci
8个回答

91

使用switch语句更容易转换为跳转表,当case标签彼此接近时,这可以使得switch语句比if-else更加高效。其思想是把一堆跳转指令依次放置在内存中,然后将值添加到程序计数器中。这样就用一个加法操作替换了一组比较操作。

以下是一些极其简化的伪汇编示例。首先是if-else版本:

    // C version
    if (1 == value)
        function1();
    else if (2 == value)
        function2();
    else if (3 == value)
        function3();

    // assembly version
    compare value, 1
    jump if zero label1
    compare value, 2
    jump if zero label2
    compare value, 3
    jump if zero label3
label1:
    call function1
label2:
    call function2
label3:
    call function3

接下来是 Switch 版本:

    // C version
    switch (value) {
    case 1: function1(); break;
    case 2: function2(); break;
    case 3: function3(); break;
    }

    // assembly version
    add program_counter, value
    call function1
    call function2
    call function3

你可以看到生成的汇编代码要紧凑得多。请注意,为处理除1、2和3之外的其他值,需要以某种方式转换该值。但是,这应该说明了概念。


1
你生成的汇编代码只有在相同大小的情况下才有效。实际上,使用nop填充和在加法之前乘以值可以做到这样的效果。 但是通常会使用跳转表,在范围检查后得到类似于“goto *table.124[value]”的东西。 - Blaisorblade
在许多情况下,间接跳转比单个跳转慢得多,因为对于间接分支的分支预测通常不太有效。 - Blaisorblade
2
好的,我想保持简单。此外,我相信C语言中的switch语句早于分支预测,他想知道它存在的原因... - Judge Maygarden
在哪些情况下你不会使用 switch,而是使用 if else? - mfaani
这不取决于编程语言吗?有些语言允许使用条件分支语句(if语句)中不存在的复杂情况(例如,正则表达式匹配字符串的情况)。 - Casey

24

我假设他是在 .net 的世界里,因为他在提到 C# 的链接。 - mmcdole
例如,C++编译器会进行一些有趣的优化。 - aku
代码示例中的 .sln 文件显示为 "# Visual C# Express 2008"。 - Kevin Haines
6
代码的优雅性因人而异。 - Ring

9

通常来说,我会忽略此类低级别的优化,因为它们通常不太重要,而且可能因编译器而异。

我认为主要的区别在于可读性。if/else非常灵活,但当您看到switch时,您立即知道所有测试都针对相同的表达式。


8
为了表达清晰,switch/case语句允许你将多个case分组在一起,例如:
case 1,2,3: do(this); break;
case 4,5,6: do(that); break;

为了提高性能,编译器有时会将switch语句优化为跳转表。

5

除了.NET中提到的代码可读性和优化之外,您还可以启用枚举等功能。

enum Color { Red, Green, Blue }; 

Color c = Color.Red;

switch (c) // Switch on the enum

{

// no casting and no need to understand what int value it is

case Color.Red:    break;
case Color.Green:  break;
case Color.Blue:   break;

}

4

有时候我们可以通过不加break语句的方式,让代码流程落入多个case中,这种技巧在IT领域非常有用,而且正如一些人所说,这样做还能提高代码运行速度。然而,最重要的也是最不重要的考虑因素是,使用这种方式让代码更加美观,比if/else语句的可读性更强。 :)


2

一些编译器可以优化Switch语句,使其更好。在某些语言中使用Switch语句存在陷阱。在Java中,Switch不能处理字符串,在VB2005中,Switch语句无法与单选按钮一起使用。
使用Switch语句可以更快且更易读,而If-Then则更通用且适用于更多场合。


if-then语句会在更多的地方起作用吗?试试这个var sillyExample = true; switch (sillyExample) { case 3 > 2: console.log("lol"); break; case 0 !== 1: console.log("好吧,我猜它在这里BREAKS"); }实际上不用管它。 - cmarangu

-1

唯一情况下开关可能更快的是当您的case值是常量,而不是动态或其他派生值,并且当case数量显着大于计算哈希到查找表中的时间时。

以Javascript为例,它编译成汇编代码在大多数引擎上执行,包括Chrome的V8引擎,在常见情况下,switch语句的执行速度比if-else语句慢30%-60%:http://jsperf.com/switch-if-else/20


3
我已经告诉过你,我的基准测试是以一种使得浏览器可以对测试进行优化的方式设置的。所有其他测试都是未优化的。是的,在未优化的代码中,elseif会更快。但是谁在意呢?我为什么要关心未优化代码的速度?如果你关心,那很好,但你应该说出来,因为大多数人并不关心。顺便说一句,这是因为benchmark.js的作者特意编写了代码,尝试禁用优化,而不是因为你必须做一些神奇的事情才能启用优化。 - Esailija
4
对我来说,在 Chrome 中速度快了10倍,怎么能说是“几乎没有改善”呢? 未经优化和经过优化的代码之间的差异是如此巨大,以至于如果你不知道编译器优化,你就无法解释这些差异而不显得不真实。 - Esailija
2
“具有讽刺意味的是,我在攻读计算机科学硕士学位时,在编译器设计方面获得了A级成绩。” 但这并不重要。重要的是是否进行了正确的性能测试(大量糟糕的测试并不比少量正确的测试更有意义)。 - Denys Séguret
2
@AnthonyHildoer 是的,你说得对!39个测试不可能都做错,尤其是像基准测试这样非常容易的东西,历史上人们一直在很好地制作它们! - Zirak
2
@AnthonyHildoer 你犯了谬误的谬误。http://plover.net/~bonds/bdksucks.html - SomeKittens
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接