C++作為C語言的升級版,支持很多C語言不支持的語法。例如,函數中的局部變量不必在函數的最開始統一定義了,在函數內部隨時定義新的局部變量成為可能。
比如下面的示例代碼,在for循環的初始條件中定義了用於計數的整形變量i,這是不符合C語言語法規定的,故而無法通過C語言編譯器的編譯。
int fun()
{
int n = 6;
for (int i = 0; i < 3; i++)
{
n += i;
}
return n;
}
必須把代碼修改為如下所示,在函數的開頭定義整形變量i,才能通過編譯。
int fun()
{
int n = 6;
int i = 0;
for (i = 0; i < 3; i++)
{
n += i;
}
return n;
}
不過,如果使用C++編譯器,以上兩段代碼都是符合語法規定的,都可以通過編譯。
回到主題,這里要說一個C++在語法方便的同時帶來的隱患。來看這一段C++的swtich..case代碼:
void fun(int nInput)
{
switch(nInput)
{
case 1:
int n;
n = 1;
printf("case1");
break;
case 2:
printf("case2");
break;
default:
printf("case defalut");
break;
}
}
int main(int argc, char* argv[])
{
fun(8);
return 0;
}
這段代碼利用了剛才提到的C++新的語法支持,在switch..case的分支case 1中,定義了整形變量n,並且把它賦值為1。這段代碼完全合法,編譯通過(VS2012環境中),運行結果如下圖所示,一切正常。
問題出現在當我們嘗試初始化整形變量n的時候。定義變量的同時初始化,是一個好習慣,然而,此時對n的初始化卻會引發錯誤導致無法編譯。修改示例代碼中的case 1部分,嘗試把整形變量n初始化為0:
void fun(int nInput)
{
switch(nInput)
{
case 1:
int n = 0;
n = 1;
printf("case1");
break;
case 2:
printf("case2");
break;
default:
printf("case defalut");
break;
}
}
int main(int argc, char* argv[])
{
fun(8);
return 0;
}
編譯報錯如下圖所示。大致意思是說n的初始化操作被跳過了。
回想函數調用過程,在函數的參數、當前代碼地址、棧地址入棧之后,緊接着系統會給函數內部的局部變量在棧里划分一片空間,這片划分出來的空間入棧之后,系統會給所有被初始化的局部變量賦予初始值。
如此一來,在示例代碼中的情況下,C++編譯器就不知所措了。整形變量n的作用域是swtich..case結構被花括號括起來的整個部分:雖然整形變量n的定義在case 1標簽下面,但它對於case 2和case default都是可見的,可以把case 2和case default理解為整形變量n的“利益相關者”。站在編譯器的角度,如果對整形變量n進行初始化操作,那么則相當於默認switch..case會跳轉到case 1標簽下,這顯然是一種置case 2和case default於不顧的非法行為;如果不進行初始化操作,那么編譯器就沒有完整翻譯程序源代碼,沒有完成自己的職責。在這種兩難境地下,編譯器只好選擇報錯了。
有沒有一種解決方案,既能讓我們充分利用C++靈活的語法規定(在switch..case結構內部也可以定義局部變量),又能夠讓我們保持定義局部變量后立即初始化的良好習慣,而且還不讓編譯器為難(報錯)呢?
答案是有的!解決思路是把定義的局部變量隔絕起來,達到縮小局部變量作用域的效果,也就是讓其他case分支看不到它。正所謂“眼不見心不煩”,其他的分支看不到這個局部變量,也就不會產生什么意見了。具體的解決方案就是在每一個case的標簽下面都嵌入一對花括號。
修改后的代碼如下所示:
void fun(int nInput)
{
switch(nInput)
{
case 1:
{
int n = 0;
n = 1;
printf("case1");
break;
}
case 2:
{
printf("case2");
break;
}
default:
{
printf("case defalut");
break;
}
}
}
int main(int argc, char* argv[])
{
fun(8);
return 0;
}
經測試,編譯通過,如下圖所示:
當然,case 2和case defalut的下面不是必須要加一對花括號,因為它們下面並沒有局部變量的定義和初始化操作。但是,在所有case label下都加上一對花括號是一個很好的習慣,因為隨着代碼量的增加,萬一這個label下面發生了變量的定義和初始化操作而沒有引起注意的話,調試起來可能會很麻煩。在那種情況下,編譯器的提示信息可能不會像本文示例這樣給出明確的錯誤描述,而是可能會因為上下文環境的原因而給出晦澀不清的錯誤描述,讓人一下看不出問題到底出在了哪里。
總結:在享受C++語法便利的同時,不要忘記了這些潛在的小陷阱,靈活利用花括號來限制局部變量的作用域。