/**************************/
/*转载 作者都不知道是谁。。。*/
/**************************/
当需要多次比较时,switch语句的效率比if-else if…… else语句(以后简称muti-if语句)的效率要高,这是我一直以来的理解,但是昨晚讨论到一个问题,这种“高效率”如何实现?今天早上又看到《更深入一点理解switch语句及c/c++对const的处理》和《透过IL看C# (1)switch语句》这两篇文章,前者(以后为[1])没有提及case语句中大跨度离散值的原理,后者(以后为[2])使用的离散数据量又比较小,而且该文侧重于用C#,由于不是很了解,不发表评论。
于是就写了一组程序,用gcc编译成汇编码(使用-S开关), 通过解读这些汇编码可以很好的帮助理解switch的原理。文中所涉及的环境为如下,Linux version 2.6.27.5-117.fc10.i686 (mockbuild@x86-7.fedora.phx.redhat.com) (gcc version 4.3.2 20081105 (Red Hat 4.3.2-7) (GCC) ) #1 SMP Tue Nov 18 12:19:59 EST 2008(取自/proc/version)
1.三个数据的比较
程序1.1
int main(void) { int i, n; switch(i){ case 101: n = 1; break; case 102: n = 2; break; case 103: n = 3; break; default: n = 0; break; } }
|
得到的汇编码1.1:
.file "switch.c" .text .globl main .type main, @function main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $24, %esp movl -12(%ebp), %eax movl %eax, -28(%ebp) cmpl $102, -28(%ebp) je .L4 cmpl $103, -28(%ebp) je .L5 cmpl $101, -28(%ebp) jne .L8 .L3: movl $1, -8(%ebp) jmp .L9 .L4: movl $2, -8(%ebp) jmp .L9 .L5: movl $3, -8(%ebp) jmp .L9 .L8: movl $0, -8(%ebp) .L9: addl $24, %esp popl %ecx popl %ebp leal -4(%ecx), %esp ret .size main, .-main .ident "GCC: (GNU) 4.3.2 20081105 (Red Hat 4.3.2-7)" .section .note.GNU-stack,"",@progbits
|
这里可以看出switch语句的效率与muti-if语句的效率基本相当,[1]中认为:“gcc确实是把一些case语句转成了李维所说的那种方式进行处理,我们看见了代码中存在有众多的 cmpl 与 jmp 语句这就相当于你使用if..else..一样”,但是如果观察仔细的话,可以看出和if语句还是有区别的(事实上我第一次也没有细看,但是在后面的实验中,我发现了switch语句的优化,回过头来才发现), switch对比较的顺序自动进行了优化, cmpl的顺序与case的顺序是不同的, 先比较的是102, 然后才是103, 101,这就相当于我们人为的对muti-if语句进行了优先顺序调整,尽管结果可能与我们预期的不同,但是编译器的确这样做了, 这点在后面的实验中尤为明显。
在[2]中,作者说C#在3个数据,且数据连续的情况下造表,在数据取值比较不连续的情况下也是造表然后填空数据,在数据取值非常不连续的情况下和muti-if比较相同,而且顺序与case的顺序相同。该文中对switch(int)的探讨也至此完结。
2.五个数据的比较
程序2.1 五个连续数据
int main(void) { int i, n; switch(i){ case 101: n = 1; break; case 102: n = 2; break; case 103: n = 3; break; case 104: n = 4; break; case 105: n = 5; break; default: n = 0; break; } }
|
汇编码2.1:
.file "switch.c" .text .globl main .type main, @function main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $24, %esp movl -12(%ebp), %eax subl $101, %eax movl %eax, -28(%ebp) cmpl $4, -28(%ebp) ja .L2 movl -28(%ebp), %edx movl .L8(,%edx,4), %eax jmp *%eax .section .rodata .align 4 .align 4 .L8: .long .L3 .long .L4 .long .L5 .long .L6 .long .L7 .text .L3: movl $1, -8(%ebp) jmp .L11 .L4: movl $2, -8(%ebp) jmp .L11 .L5: movl $3, -8(%ebp) jmp .L11 .L6: movl $4, -8(%ebp) jmp .L11 .L7: movl $5, -8(%ebp) jmp .L11 .L2: movl $0, -8(%ebp) .L11: addl $24, %esp popl %ecx popl %ebp leal -4(%ecx), %esp ret .size main, .-main .ident "GCC: (GNU) 4.3.2 20081105 (Red Hat 4.3.2-7)"
|
可以看出,这里switch确实如同[1][2]中所述,编译器自造了.L8指向的表,表中标明了case跳转的入口,由此可见,这种情况下swithc效率确实比muti-if高,通过减去最小值所的的偏移来在表中寻找跳转入口。
程序2.2 五个比较连续数据
int main(void) { int i, n; switch(i){ case 101: n = 1; break; case 103: n = 2; break; case 106: n = 3; break; case 109: n = 4; break; case 112: n = 5; break; default: n = 0; break; } }
|
汇编码2,2:
.file "switch.c" .text .globl main .type main, @function main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $24, %esp movl -12(%ebp), %eax subl $101, %eax movl %eax, -28(%ebp) cmpl $11, -28(%ebp) ja .L2 movl -28(%ebp), %edx movl .L8(,%edx,4), %eax jmp *%eax .section .rodata .align 4 .align 4 .L8: .long .L3 .long .L2 .long .L4 .long .L2 .long .L2 .long .L5 .long .L2 .long .L2 .long .L6 .long .L2 .long .L2 .long .L7 .text .L3: movl $1, -8(%ebp) jmp .L11 .L4: movl $2, -8(%ebp) jmp .L11 .L5: movl $3, -8(%ebp) jmp .L11 .L6: movl $4, -8(%ebp) jmp .L11 .L7: movl $5, -8(%ebp) jmp .L11 .L2: movl $0, -8(%ebp) .L11: addl $24, %esp popl %ecx popl %ebp leal -4(%ecx), %esp ret .size main, .-main .ident "GCC: (GNU) 4.3.2 20081105 (Red Hat 4.3.2-7)" .section .note.GNU-stack,"",@progbits
|
这里也如[2]所述, 建立了一个长度为case中最大值与最小值之差的表, case中未定义的则转到defalut来执行。
程序2.3 五个非常不连续的数据
int main(void) { int i, n; switch(i){ case 100: n = 1; break; case 120: n = 2; break; case 140: n = 3; break; case 160: n = 4; break; case 180: n = 5; break; default: n = 0; break; } }
|
汇编码2.3:
.file "switch.c" .text .globl main .type main, @function main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $24, %esp movl -12(%ebp), %eax movl %eax, -28(%ebp) cmpl $140, -28(%ebp) je .L5 cmpl $140, -28(%ebp) jg .L8 cmpl $100, -28(%ebp) je .L3 cmpl $120, -28(%ebp) je .L4 jmp .L2 .L8: cmpl $160, -28(%ebp) je .L6 cmpl $180, -28(%ebp) je .L7 jmp .L2 .L3: movl $1, -8(%ebp) jmp .L11 .L4: movl $2, -8(%ebp) jmp .L11 .L5: movl $3, -8(%ebp) jmp .L11 .L6: movl $4, -8(%ebp) jmp .L11 .L7: movl $5, -8(%ebp) jmp .L11 .L2: movl $0, -8(%ebp) .L11: addl $24, %esp popl %ecx popl %ebp leal -4(%ecx), %esp ret .size main, .-main .ident "GCC: (GNU) 4.3.2 20081105 (Red Hat 4.3.2-7)" .section .note.GNU-stack,"",@progbits
|
可以看到,这里switch又进行了二分法查找的优化, 而且当我把程序中“100, 120, 140, 160, 180”的顺序任意打乱后,得到的汇编码都是相同的,由此可以推测,switch在编译时会先获得case中的各值,然后进行排序,最后生成使用二分法优化的查找比较模式。
另外,我推测使用造表法和二分查表法的应该是依据case中最大值与最小值之差与case语句个数来取舍的。
3 更多数据的比较
这里我又进行了更多条case语句的比较,程序源代码和生成的汇编码可以通过附件下载,通过比较,结论与我上面的推测基本相同。具体的原理相信在讲解C/C++编译器原理的书中有提及,希望有人找到后能够发送给我以共同探讨(email:yangyuan.whuyy@yahoo.com.cn)
4 为什么C/C++中switch语句需要的是整型或字符型
我们知道,在C中,字符型实际上也是作为整型来存储的,所以这个问题实际上是指switch语句只支持整型数据。由上面的讨论可以知道char的结果与整型应该是相同的
分享到:
相关推荐
在给定的文件 `Control_switchCase2_switchCase2.pdf` 中,示例代码展示了如何使用 `switch` 语句处理串行输入,并通过控制 LED 的亮灭来响应不同字符。以下是对这个知识点的详细解释: 首先,`switch` 语句通常...
在本文中,我们将深入探讨`switch`语句的工作原理,尤其是通过汇编代码的角度来理解它。 首先,让我们看看`switch`语句的基本语法。一个典型的`switch`语句如下: ```c switch (expression) { case value1: // ...
通过分析"Case_Switch.vi",用户不仅可以了解Case和Switch的工作原理,还能学习到如何组织和设计具有复杂逻辑的LabVIEW程序。 总结来说,"Case_Switch.rar"中的案例提供了使用LabVIEW中的Case和Switch结构进行条件...
在本练习中,我们将深入探讨`switch`语句的工作原理、语法以及如何有效地使用它。 `switch`语句的基本语法如下: ```javascript switch(expression) { case value1: // 当expression的值等于value1时,执行这里...
本文将深入探讨`switch`语句的基本语法、工作原理以及在实际编程中的简单应用。 `switch`语句的基本结构如下: ```c switch(expression) { case constant1: // statements when expression equals constant1 ...
下面我们将深入探讨`switch`语句和`case`语句的工作原理、语法特点以及在实际编程中的应用。 `switch`语句是基于表达式值进行判断的多路选择结构。它的基本语法如下: ```markdown switch (expression) { case ...
这个课程设计的难点在于构建一个能够处理特定文法的编译器,尤其是针对描述中提到的"有switch_case无for"的规则。这表明编译器需要理解和处理不包含`for`循环但包含`switch`语句的语法结构。 编译器是将高级编程...
根据提供的标题、描述、标签及部分内容,我们可以了解到这篇文章主要讲述的是关于Creator软件中的Switch节点功能。...希望本文能帮助读者更好地理解Switch节点的工作原理及其在实际项目中的应用技巧。
Java Switch语句使用原理及实例解析 Java Switch语句是Java语言中的一种选择结构,用于根据不同的条件执行不同的代码块...但是,在使用Switch语句时,需要注意避免错误思维,正确理解Switch语句的执行原理和使用场景。
下面我们将深入探讨`case`语句的原理、用法以及它在实际开发中的应用。 `case`语句通常与`switch`结构一起使用,创建一个多路选择结构。在`switch`语句中,`case`语句定义了可能的选项,每个`case`后面跟着一个...
在C语言编程中,分支结构是实现不同执行路径的关键。除了常见的if-else语句,C语言还提供了一种称为switch语句的分支结构,它适用于当需要根据一...理解其工作原理和正确的使用方法对于编写可靠且高效的程序至关重要。
- **可读性**:相比于嵌套的`if...else`,`switch`结构使得代码更加清晰,易于理解。 - **效率**:在某些编译器中,`switch`可能被优化成高效的查找表,提高执行速度。 **5. 实际应用** `switch`语句常用于处理多...
《深入理解switch函数及其应用》 在编程语言中,switch语句是一种条件控制结构,它提供了多路选择的执行路径,使得程序...通过实际的编程练习,我们可以更好地理解switch语句的工作原理,并将其灵活运用到各种场景中。
为了更深入地理解switch语句的工作原理,让我们看看如何通过一个具体的示例来理解switch是如何执行的。以下是一个PHP代码示例,它演示了如何处理变量`$i`的值,并根据这些值输出不同的消息: ```php $i = 2; ...
在C++编程语言中,`switch`语句是一种条件控制结构,用于执行多个可能的代码块之...学生可能需要根据`LabManual-3.cbp`中的项目设置和`main.cpp`中的示例代码来完成任务,同时理解和掌握`switch`语句的工作原理和用法。
在编程教学中,理解switch语句的工作原理和正确使用方法对学生提升编程能力有着重要意义。本文通过对switch语句常见错误的分析,以及其与嵌套、循环结构的综合使用案例的探讨,旨在提高学生的综合编程能力和动手积极...
总的来说,Java Switch语句是编写条件控制代码的一个强大工具,通过理解其工作原理和使用方法,可以提升代码的可读性和效率。在学习过程中,通过实践和不断练习,能够更好地掌握这一特性,并将其应用于实际项目中。
在C语言中,`switch`语句是一种控制流程结构,用于执行多...总结,`switch`语句是C语言中一种强大的分支控制结构,能够帮助我们编写简洁且易于理解的代码。了解并熟练运用`switch`语句,可以提高代码的可读性和维护性。
本篇文章将深入探讨`switch`语句的工作原理、语法以及其在实际编程中的应用。 1. **switch语句的基本语法**: ```cpp switch(expression) { case value1: // code block for value1 break; case value2: // ...
让我们深入理解`switch`语句的工作原理以及在示例中涉及的相关知识点。 1. `switch`语句的基本结构: `switch`语句由`switch`关键字、一个条件表达式和一系列的`case`标签组成。当条件表达式的值与`case`后的常量...