淺析C/C++中的switch/case陷阱
先看下面一段代碼:
文件main.cpp
#include<iostream> using namespace std; int main(int argc, char *argv[]) { int a =0; switch(a) { case 0: int b=1;cout<<b<<endl;break; case 1: cout<<b<<endl;break; default:break; } return 0; }
在gcc編譯器下編譯的結果為:
提示跳過了變量b的初始化過程。對於一個局部變量,它的作用域為它所定義的地方到它所在的語句塊結束為止,那么對於變量b,它所在的最小語句塊為switch{}塊,那么也就說在case 0后面的部分,變量b都是可見的(注意在case 0之前變量b是無法訪問的)。考慮這樣一種情況,當a的值為1,那么程序就跳到case 1執行,此時b雖然可以訪問,但是跳過了它的初始化過程。而如果在定義變量的同時進行了初始化,表明程序員希望初始化這個變量,但是此時跳過了該變量的初始化,就可能導致程序出現程序員無法意料的情況,因此編譯器為了避免跳過這樣的初始化而造成無法預料的結果,就對該語句進行報錯。
如果將上述代碼改為:
switch(a) { case 0: int b;b=0;cout<<b<<endl;break; case 1: cout<<b<<endl;break; default: break; }
編譯的結果為:
只是進行了警告,因為在定義變量的時候沒有進行初始化,也就是說程序怎么執行都不會跳過變量的初始化過程,不會說由於跳過了該過程而造成無法預料的錯誤或程序崩潰現象。因此只是進行了警告。
再看下面這段代碼:
switch(a) { case 0: break; default: int b=1;cout<<b<<endl;break; }
這段代碼沒有報錯。因為如果執行case 0,變量b沒有進行初始化,但是由於在case 0部分b是不可見的,因此不會對程序造成任何影響,而如果執行default分支,則b會被初始化,因此程序沒有報錯。
歸根到底,出現上述的crosses initialization和jump to case label錯誤的原因是由於變量的作用域問題,因此一個好的習慣就是在case子句下面加上大括號來限定變量的作用域。
switch(a) { case 0: {int b=1;cout<<b<<endl;break;} case 1: break; default: break; }
不過要注意,一旦加上了大括號,在case 0后面便不能訪問到變量b了。
注意,如果上述代碼以C方式進行編譯,編譯結果則會有所不同:
main.c
#include <stdio.h> #include<stdlib.h> int main(int argc, char *argv[]) { int a =2; switch(a) { case 0: int b=17;printf("%d\n",b);break; case 1: break; default:printf("%d\n",b);break; } return 0; }
編譯結果:
報的錯誤意思是在該標簽下不能定義變量。為什么呢?原因很簡單,在C語言中所有的變量定義都必須放到所在語句塊的最前面。所以如果改成這樣:
#include <stdio.h> #include<stdlib.h> int main(int argc, char *argv[]) { int a =2; switch(a) { int b=17; case 0: printf("%d\n",b);break; case 1: break; default:printf("%d\n",b);break; } return 0; }
編譯結果:
沒有報錯,注意這里沒有報crosses initialization的錯誤,這點C和C++有點不同。如果以C++方式編譯,肯定會報crosses initialization的錯誤,這也更加說明C++在語法方面要求比C更加嚴格。
該程序運行的結果:
輸出32,是一個隨機值。因為b並沒有進行初始化,所以輸出的是隨機值。
以上只是個人觀點,若有不正之處,懇請指正,以免誤導他人。