亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C++性能剖析教程之switch語句

 更新時(shí)間:2018年06月16日 08:04:11   作者:Root lee  
除了用嵌套if語句外,C++中還提供switch語句,又稱為“開關(guān)語句”,用來實(shí)現(xiàn)多分支(多選一),下面這篇文章主要給大家介紹了關(guān)于C++性能剖析教程之switch語句的相關(guān)資料,需要的朋友可以參考下

前言

幾乎每本面向初學(xué)者的C語言或C++書籍在前面兩章都會(huì)提到分支控制語句if……else和switch……case,在某些情況下這兩種分支控制語句可以互相替換,但卻很少有人去深究在if……else和switch……case語句的背后到底有什么異同?應(yīng)該選擇哪一個(gè)語句才能使得效率最高?要回答這些問題,只能走到switch語句的背后,看看這些語句到底是怎么實(shí)現(xiàn)的。

基本格式

switch語句的基本格式如下:

switch (表達(dá)式) {
case 常量表達(dá)式1:《語句序列1》《break;》 //《》中的內(nèi)容可省
……
case 常量表達(dá)式n:《語句序列n》《break;》 //同上,下同
《default:語句序列》
}

其中:

  • 表達(dá)式——稱為“條件表達(dá)式”,用作判斷條件,取值為整型、字符型、布爾型或枚舉型。
  • 常量表達(dá)式——由常量構(gòu)成,取值類型與條件表達(dá)式相同。
  • 語句序列——可以是一個(gè)語句也可以是一組語句。

if語句與switch語句

相信學(xué)過C/C++的同學(xué)對(duì)這兩個(gè)語句的異同早就了如指掌,if語句作為條件判斷,滿足條件進(jìn)入if語句塊,不滿足條件則進(jìn)入else語句塊,而且if和else語句塊又可以繼續(xù)嵌套if語句。switch則是通過判斷一個(gè)整型表達(dá)式的值來決定進(jìn)入到哪一個(gè)case語句中,如果所有case條件都不滿足則進(jìn)入到default語句塊。

//簡(jiǎn)單的if語句
if (a == 1)
 i = 1;
else if (a == 2)
 i = 2;
else
 i = 3;
//簡(jiǎn)單的switch語句
switch (a){
 case 1: i = 1;
 case 2: i = 2;
 default: i = 3;
}

編譯器如何實(shí)現(xiàn)switch語句?

現(xiàn)在編譯器已經(jīng)足夠智能和強(qiáng)大,經(jīng)過測(cè)試,g++實(shí)現(xiàn)switch語句的方式就至少有三種,編譯器會(huì)根據(jù)代碼的實(shí)際情況,權(quán)衡時(shí)間效率和空間效率,去選擇一個(gè)對(duì)當(dāng)前代碼而言綜合效率最高的一種。

編譯器實(shí)現(xiàn)switch語句的三種方式:

  • 逐條件判斷
  • 跳轉(zhuǎn)表
  • 二分查找法

后面我們將就這三種實(shí)現(xiàn)方法適用的代碼場(chǎng)景進(jìn)行測(cè)試和分析。

1. 逐條件判斷法

逐條件判斷法其實(shí)就是和if……else語句的匯編實(shí)現(xiàn)相同,編譯器把switch語句中各個(gè)case條件逐個(gè)進(jìn)行判斷,直到找到正確的case語句塊。這種方法適用于switch語句中case條件很少的情況,即使逐個(gè)條件判斷也不會(huì)導(dǎo)致大量時(shí)間和空間的浪費(fèi),比如下面這段代碼:

#include <algorithm>
int test_switch(){
 int i ;
 int a = std::rand();
 switch(a){
  case 0: i = 0;break;
  case 1: i = 1;break;
  case 2: i = 2;break;
  default: i = 3;break;
 }
 return i;
}

該代碼對(duì)應(yīng)的匯編代碼如下:

 movl -4(%rbp), %eax
 cmpl $1, %eax
 je .L3
 cmpl $2, %eax
 je .L4
 testl %eax, %eax
 jne .L8
 movl $0, -8(%rbp)
 jmp .L6
.L3:
 movl $1, -8(%rbp)
 jmp .L6
.L4:
 movl $2, -8(%rbp)
 jmp .L6
.L8:
 movl $3, -8(%rbp)
 nop

eax寄存器存儲(chǔ)的是判斷條件值(對(duì)應(yīng)于C++代碼中的a值),首先判斷a是否等于1,如果等于1則跳轉(zhuǎn)到.L3執(zhí)行a==1對(duì)應(yīng)的代碼段,然后判斷a是否等于2,如果等于2則跳轉(zhuǎn)到.L4執(zhí)行a==2對(duì)應(yīng)的代碼段……可能難理解的是第6行代碼testl %eax, %eax,其實(shí)這只是編譯器提高判斷一個(gè)寄存器是否為0效率的一個(gè)小技巧,如果eax不等于0則跳轉(zhuǎn)到.L8代碼段,執(zhí)行default代碼段對(duì)應(yīng)的代碼,如果eax等于0則執(zhí)行a==0對(duì)應(yīng)的代碼段。

由上面對(duì)編譯器生成匯編代碼的分析,我們可以發(fā)現(xiàn):編譯器在這種情況下使用逐個(gè)條件判斷來實(shí)現(xiàn)switch語句。

2. 跳轉(zhuǎn)表實(shí)現(xiàn)法

在編譯器采用這種switch語句實(shí)現(xiàn)方式的時(shí)候,會(huì)在程序中生成一個(gè)跳轉(zhuǎn)表,跳轉(zhuǎn)表存放各個(gè)case語句指令塊的地址,程序運(yùn)行時(shí),首先判斷switch條件的值,然后把該條件值作為跳轉(zhuǎn)表的偏移量去找到對(duì)應(yīng)case語句的指令地址,然后執(zhí)行。這種方法適用于case條件較多,但是case的值比較連續(xù)的情況,使用這種方法可以提高時(shí)間效率且不會(huì)顯著降低空間效率,比如下面這段代碼編譯器就會(huì)采用跳轉(zhuǎn)表這種實(shí)現(xiàn)方式:

#include <algorithm>

int test_switch(){
 int i ;
 int a = std::rand();
 switch(a){
  case 0: i = 0;break;
  case 1: i = 1;break;
  case 2: i = 2;break;
  case 3: i = 3;break;
  case 4: i = 4;break;
  case 5: i = 5;break;
  case 6: i = 6;break;
  case 7: i = 7;break;
  case 8: i = 8;break;
  case 9: i = 9;break;
  default: i = 10;break;
 }
 return i;
}

該代碼對(duì)應(yīng)的匯編代碼如下:

 movl -4(%rbp), %eax
 movq .L4(,%rax,8), %rax
 jmp *%rax
.L4:
 .quad .L3
 .quad .L5
 .quad .L6
 .quad .L7
 .quad .L8
 .quad .L9
 .quad .L10
 .quad .L11
 .quad .L12
 .quad .L13
 .text
.L3:
 movl $0, -8(%rbp)
 jmp .L14
.L5:
 movl $1, -8(%rbp)
 jmp .L14
#后面省略……

在x64架構(gòu)中,eax寄存器是rax寄存器的低32位,此處我們可以認(rèn)為兩者值相等,代碼第一行是把判斷條件(對(duì)應(yīng)于C++代碼中的a值)復(fù)制到eax寄存器中,第二行代碼是把.L4段偏移rax寄存器值大小的地址賦值給rax寄存器,第三行代碼則是取出rax中存放的地址并且跳轉(zhuǎn)到該地址處。我們可以清楚的看到.L4代碼段就是編譯器為switch語句生成的存放于.text段的跳轉(zhuǎn)表,每種case均對(duì)應(yīng)于跳轉(zhuǎn)表中一個(gè)地址值,我們通過判斷條件的值即可計(jì)算出來其對(duì)應(yīng)代碼段地址存放的地址相對(duì)于.L4的偏移,從而實(shí)現(xiàn)高效的跳轉(zhuǎn)。

3. 二分查找法

如果case值較多且分布極其離散的話,如果采用逐條件判斷的話,時(shí)間效率會(huì)很低,如果采用跳轉(zhuǎn)表方法的話,跳轉(zhuǎn)表占用的空間就會(huì)很大,前兩種方法均會(huì)導(dǎo)致程序效率低。在這種情況下,編譯器就會(huì)采用二分查找法實(shí)現(xiàn)switch語句,程序編譯時(shí),編譯器先將所有case值排序后按照二分查找順序?qū)懭雲(yún)R編代碼,在程序執(zhí)行時(shí)則采二分查找的方法在各個(gè)case值中查找條件值,如果查找到則執(zhí)行對(duì)應(yīng)的case語句,如果最終沒有查找到則執(zhí)行default語句。對(duì)于如下C++代碼編譯器就會(huì)采用這種二分查找法實(shí)現(xiàn)switch語句:

#include <algorithm>

int test_switch(){
  int i ;
  int a = std::rand();
  switch(a){
    case 4: i = 4;break;
    case 10: i = 10;break;
    case 50: i = 50;break;
    case 100: i = 100;break;
    case 200: i = 200;break;
    case 500: i = 500;break;
    default: i = 0;break;
  }
  return i;
}

改代碼段對(duì)應(yīng)的匯編代碼為:

    movl -4(%rbp), %eax
 cmpl $50, %eax
 je .L3
 cmpl $50, %eax
 jg .L4
 cmpl $4, %eax
 je .L5
 cmpl $10, %eax
 je .L6
 jmp .L2
.L4:
 cmpl $200, %eax
 je .L7
 cmpl $500, %eax
 je .L8
 cmpl $100, %eax
 je .L9
 jmp .L2

代碼第二行條件值首先與50比較,為什么是50而不是放在最前面的4?這是因?yàn)槎植檎沂紫炔檎业氖翘幱谥虚g的值,所以這里先與50進(jìn)行比較,如果eax等于50,則執(zhí)行case

50對(duì)應(yīng)代碼,如果eax值大于50則跳轉(zhuǎn)到.L4代碼段,如果eax小于50則繼續(xù)跟4比較……直至找到條件值或者查找完畢條件值不存在。可以看出二分查找法在保持了較高的查詢效率的同時(shí)又節(jié)省了空間占用。

總結(jié)

何時(shí)應(yīng)該使用if……else語句,何時(shí)應(yīng)該使用switch……case語句?

通過上面的分析我們可以得出結(jié)論,在可能條件比較少的時(shí)候使用if……else和switch……case所對(duì)應(yīng)的匯編代碼是相同的,所以兩者在性能上是沒有區(qū)別的,使用哪一種取決于個(gè)人習(xí)慣。如果條件較多的話,顯而易見switch……case的效率更高,無論是跳轉(zhuǎn)表還是二分查找都比if……else的順序查找效率更高,所以在這種情況下盡量選用switch語句來實(shí)現(xiàn)分支語句。當(dāng)然如果我們知道哪種條件出現(xiàn)的概率最高,我們可以將這個(gè)條件放在if判斷的第一個(gè),使順序查找提前結(jié)束,這時(shí)使用if……else語句也可以達(dá)到較高的運(yùn)行效率。

switch語句也有他本身的局限性,即switch語句的值只能為整型,比如當(dāng)我們需要對(duì)一個(gè)double型數(shù)據(jù)進(jìn)行判斷時(shí),便無法使用switch語句,這時(shí)只能使用if……else語句來實(shí)現(xiàn)。

好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • 解析C語言中如何正確使用const

    解析C語言中如何正確使用const

    本篇文章是對(duì)C語言中如何正確使用const,進(jìn)行了詳細(xì)的分析介紹。需要的朋友參考下
    2013-05-05
  • 一文搞懂C語言中的文件操作

    一文搞懂C語言中的文件操作

    文件操作想必大家掌握的并不熟練,確實(shí)因?yàn)槲覀冇玫牟⒉欢?,而本?jié)內(nèi)容能夠讓大家初步認(rèn)識(shí)文件操作,從文件認(rèn)識(shí)到文件使用,讓我們對(duì)c語言文件操作有個(gè)初步的了解
    2022-11-11
  • 用c語言根據(jù)可變參數(shù)合成字符串的實(shí)現(xiàn)代碼

    用c語言根據(jù)可變參數(shù)合成字符串的實(shí)現(xiàn)代碼

    本篇文章是對(duì)用c語言根據(jù)可變參數(shù)合成字符串的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C++超詳細(xì)講解標(biāo)準(zhǔn)庫

    C++超詳細(xì)講解標(biāo)準(zhǔn)庫

    C++強(qiáng)大的功能來源于其豐富的類庫及庫函數(shù)資源。C++標(biāo)準(zhǔn)庫(C++ Standard Library, 亦可稱作,C++標(biāo)準(zhǔn)程序庫)的內(nèi)容總共在50個(gè)標(biāo)準(zhǔn)頭文件中定義。在C++開發(fā)中,要盡可能地利用標(biāo)準(zhǔn)庫完成
    2022-06-06
  • opencv3/C++實(shí)現(xiàn)光流點(diǎn)追蹤

    opencv3/C++實(shí)現(xiàn)光流點(diǎn)追蹤

    今天小編就為大家分享一篇opencv3/C++實(shí)現(xiàn)光流點(diǎn)追蹤,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2019-12-12
  • C語言實(shí)現(xiàn)對(duì)文件進(jìn)行操作的示例詳解

    C語言實(shí)現(xiàn)對(duì)文件進(jìn)行操作的示例詳解

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)對(duì)文件進(jìn)行操作的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C語言有一定的幫助,需要的可以參考一下
    2023-04-04
  • notepad介紹及插件cmake編譯過程(替代notepad++)

    notepad介紹及插件cmake編譯過程(替代notepad++)

    這篇文章主要介紹了notepad介紹及插件cmake編譯過程(替代notepad++),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • 利用C++ R3層斷鏈實(shí)現(xiàn)模塊隱藏功能

    利用C++ R3層斷鏈實(shí)現(xiàn)模塊隱藏功能

    在R3層的模塊隱藏,我們需要做的就是將其該鏈表斷鏈,將某一模塊從這個(gè)雙向鏈表中摘除,這樣再調(diào)用傳統(tǒng)的API時(shí)就會(huì)搜索不到。本文重點(diǎn)給大家介紹利用C++ R3層斷鏈實(shí)現(xiàn)模塊隱藏功能,感興趣的朋友一起看看吧
    2019-10-10
  • C++ 遍歷二叉樹實(shí)例詳解

    C++ 遍歷二叉樹實(shí)例詳解

    這篇文章主要介紹了C++ 遍歷二叉樹實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • C語言實(shí)現(xiàn)手寫Map(全功能)的示例代碼

    C語言實(shí)現(xiàn)手寫Map(全功能)的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何利用C語言實(shí)現(xiàn)手寫Map(全功能),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C語言有一定幫助,需要的可以參考一下
    2022-08-08

最新評(píng)論