C++中使用switch..case語句的易出錯陷阱和規避方法


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++語法便利的同時,不要忘記了這些潛在的小陷阱,靈活利用花括號來限制局部變量的作用域。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM