我一直想知道-爲何您不能在switch語句中的case標籤以後聲明變量? 在C ++中,您幾乎能夠在任何地方聲明變量(而且聲明它們接近首次使用顯然是一件好事),但如下操做仍然無效: ios
switch (val) { case VAL: // This won't work int newVal = 42; break; case ANOTHER_VAL: ... break; }
上面給了我如下錯誤(MSC): spa
「 newVal」的初始化被「 case」標籤跳過 scala
這彷佛也是其餘語言的限制。 爲何會有這樣的問題? code
C ++ Standard具備:能夠傳輸到塊中,但不能以初始化繞過聲明的方式進行。 除非變量具備POD類型(3.9)且聲明時沒有初始化程序(8.5),不然該程序將從具備自動存儲持續時間的局部變量不在範圍內的點跳轉到其處於範圍內的點的格式不正確。 orm
用來講明此規則的代碼: 對象
#include <iostream> using namespace std; class X { public: X() { cout << "constructor" << endl; } ~X() { cout << "destructor" << endl; } }; template <class type> void ill_formed() { goto lx; ly: type a; lx: goto ly; } template <class type> void ok() { ly: type a; lx: goto ly; } void test_class() { ok<X>(); // compile error ill_formed<X>(); } void test_scalar() { ok<int>(); ill_formed<int>(); } int main(int argc, const char *argv[]) { return 0; }
顯示初始化效果的代碼: get
#include <iostream> using namespace std; int test1() { int i = 0; // There jumps fo "case 1" and "case 2" switch(i) { case 1: // Compile error because of the initializer int r = 1; break; case 2: break; }; } void test2() { int i = 2; switch(i) { case 1: int r; r= 1; break; case 2: cout << "r: " << r << endl; break; }; } int main(int argc, const char *argv[]) { test1(); test2(); return 0; }
到目前爲止,答案一直是C ++。 it
對於C ++,您不能跳過初始化。 您能夠在C中使用。可是,在C中,聲明不是語句,而且大小寫標籤後面必須帶有語句。 io
所以,有效(但難看)的C,無效的C ++ 編譯
switch (something) { case 1:; // Ugly hack empty statement int i = 6; do_stuff_with_i(i); break; case 2: do_something(); break; default: get_a_life(); }
相反,在C ++中,聲明是一條語句,所以如下內容是有效的C ++,無效的C
switch (something) { case 1: do_something(); break; case 2: int i = 12; do_something_else(); }
彷佛能夠在switch case語句中聲明或建立匿名對象,緣由是它們沒法被引用,所以不能陷入下一種狀況。 考慮此示例在GCC 4.5.3和Visual Studio 2008上編譯(多是合規性問題,所以請專家們權衡一下)
#include <cstdlib> struct Foo{}; int main() { int i = 42; switch( i ) { case 42: Foo(); // Apparently valid break; default: break; } return EXIT_SUCCESS; }
這個問題
是
最初標記爲[C]和[C ++]在同一時間。 原始代碼確實在C和C ++中均無效,可是出於徹底不一樣的不相關緣由。
在C ++中,此代碼無效,由於case ANOTHER_VAL:
標籤跳過了其初始化而跳入了變量newVal
的範圍。 在C ++中,繞過自動對象初始化的跳轉是非法的。 大多數回答正確地解決了問題的這一方面。
可是,在C語言中,繞過變量初始化不是錯誤。 在C語言中,進入變量的初始化範圍是合法的。這僅表示該變量未初始化。 出於徹底不一樣的緣由,原始代碼沒法在C中編譯。 標籤case VAL:
在原始代碼中附加到變量newVal
的聲明中。 在C語言中,聲明不是語句。 它們不能被標記。 這就是當將此代碼解釋爲C代碼時致使錯誤的緣由。
switch (val) { case VAL: /* <- C error is here */ int newVal = 42; break; case ANOTHER_VAL: /* <- C++ error is here */ ... break; }
添加一個額外的{}
塊能夠解決C ++和C問題,即便這些問題剛好有很大不一樣。 在C ++方面,它限制了newVal
的範圍,確保case ANOTHER_VAL:
再也不跳入該範圍,從而消除了C ++問題。 在C方面,多餘的{}
引入了複合語句,從而使case VAL:
標籤適用於一條語句,從而消除了C問題。
在C狀況下,無需使用{}
便可輕鬆解決問題。 只需在case VAL:
標籤後添加一個空語句,代碼便會有效
switch (val) { case VAL:; /* Now it works in C! */ int newVal = 42; break; case ANOTHER_VAL: ... break; }
請注意,即便從C的角度來看它如今是有效的,但從C ++的角度來看它仍然是無效的。
對稱地,在C ++狀況下,無需使用{}
便可輕鬆解決問題。 只需從變量聲明中刪除初始化程序,代碼將變爲有效
switch (val) { case VAL: int newVal; newVal = 42; break; case ANOTHER_VAL: /* Now it works in C++! */ ... break; }
請注意,即便現在從C ++角度來看它是有效的,但從C角度來看它仍然無效。
我只是想強調苗條的觀點 。 開關構造可建立一個完整的一流公民範圍。 所以,能夠在第一個case標籤以前的switch語句中聲明(並初始化)變量, 而無需附加的括號對:
switch (val) { /* This *will* work, even in C89 */ int newVal = 42; case VAL: newVal = 1984; break; case ANOTHER_VAL: newVal = 2001; break; }