1. 程式人生 > >switch case 語句內部變數定義

switch case 語句內部變數定義

switch case語句是非常常用的語句,入門的碼農也知道是做什麼的。

但關於switch case內定義變數的問題,網上的很多博文都有謬誤,在這裡我寫一下對這個語句的瞭解。

先看合法的定義方式:

int main(int argc, const char * argv[]) {
    int idx = 2;
    switch (idx) {
            int k;
        case 1:
            int j;
            break;
        case 2:
            k = 1;
            j = 2;
            std::cout<<"K:"<<k<<std::endl;
            std::cout<<"J:"<<j<<std::endl;
            break;
        default:
            break;
    }
    return 0;
}


在C++11 std Dialect下 打印出的結果是:
K:1
J:2

然後看看不合法的寫法:

int main(int argc, const char * argv[]) {
    int idx = 2;
    switch (idx) {
            int k = 1;
        case 1:
            int j = 1;
            break;
        case 2:
            k = 1;
            j = 2;
            std::cout<<"K:"<<k<<std::endl;
            std::cout<<"J:"<<j<<std::endl;
            break;
        default:
            break;
    }
    return 0;
}

編譯無法通過。

解釋下原因:

一為什麼合法?

首先一個變數有沒有被定義是在編譯時就檢查過的,因此這裡可以編譯通過。

其次在處理switch case語句中,C++11標準的編譯器都會在執行case跳轉前為變數分配空間,因此執行也沒有問題。

(當然我並不建議在case語句外定義變數,因為何時為變數分配空間是編譯器特性,而非語言特性,雖然這個問題中遵守C++11標準的編譯器沒有問題)

二為什麼不合法?

C++11標準禁止這種寫法。在編譯時,編譯器就會報錯。C++11禁止這種寫法的原因來自於C++的一個規定:

It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps from a point where a local variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has POD type (3.9) and is declared without an initializer

. (The transfer from the condition of a switch statement to a case label is considered a jump in this respect.)

眾所周知,C++不允許使用未初始化的變數,而初始化操作和定義變數對於編譯器來說是兩碼事,初始化操作是一個確確實實的在執行時才會被呼叫的語句,是可以被case跳轉遮蔽掉的語句,而定義則是在編譯器就完成檢查的。

如果二的寫法合法的話,那麼會發生這樣的問題:

編譯時定義變數被執行,初始化和賦值語句也用各自的方式編譯完成,並且在執行時,程式也的確為此變數分配了空間。但是在執行時,初始化語句卻在某些情況下被跳轉掉了。

雖然對於int等型別的變數,賦值和初始化在我們看來都是"=",但編譯後的二進位制是完全不同的,編譯器會在編譯時把"int j = 1;"編譯成初始化,而把"j = 1;"編譯成賦值。因此在整個執行過程中,如果初始化語句被case跳轉掉了,我們在其他case語句中的"="不可能對此變數進行初始化。

這時,如果在整個switch(){}的定義域中,有對此變數的呼叫,那就是『試圖在初始化一個變數前使用它』,因此C++11禁止了這種寫法。

額外談一些:

如果不在case中加上{},整個switch(){}都是使用同一個作用域,這一點通過上文符合語法例子中對J、K的定義和使用就能看出來。最好的方法就是在編碼時,將整個switch語句用到的變數在switch外宣告,並且針對某個case需要單獨使用變數的情況,用{}明確此case語句的作用域

case的實現與goto非常相似,case的本質是一種標籤,在switch case語句中變數的定義問題可以推廣到goto語句中。

下面的程式碼也是不合法的,因為string是一個類,類有自己的隱式初始化方法,實際上這依然是個可能被跳轉掉的初始化語句。

        case 1:
            std::string tempStr;
            break;