當前位置:才華齋>計算機>C語言>

C++跳轉語句之Goto對變數定義的影響詳解

C語言 閱讀(2.79W)

goto語句也稱為無條件轉移語句,以下是為大家分享的C++跳轉語句之Goto對變數定義的影響詳解,供大家參考借鑑,歡迎瀏覽!

C++跳轉語句之Goto對變數定義的影響詳解

 前言

goto語句也稱為無條件轉移語句,其基本形式如下 :

語句標號由一個有效地識別符號和符號";"組成,其中,識別符號的命名規則與變數名稱相同,即由字母、數字和下劃線組成,且第一個字元必須是字母或下劃線。執行goto語句後,程式就會跳轉到語句標號處,並執行其後的語句。

通常goto語句與if條件語句連用,但是,goto語句在給程式帶來靈活性的同時,也會使得使程式結構層次不清,而且不易讀,所以要合理運用該語句。

 發現問題

我們經常碰到有在goto後面定義變數,linux下編譯不通過的問題(報錯資訊:crosses initialization of)。其實,只要注意一下就好了,今天問了一下公司前輩之後,也翻了些資料,記錄一下,加深記憶,也希望能對一些人有些許幫助。

錯誤示例程式碼:

#include <iostream>

using namespace std;

int main()

{

goto Exit;

int a = 0;

Exit:

return 0;

}

報錯:

[root@localhost c-c++]# g++ goto_

goto_: In function 'int main()':

goto_: error: jump to label 'Exit'

goto_: error: from here

goto_: error: crosses initialization of 'int a'

  正確寫法

也不能說是正確的寫法,只能說是編譯OK的寫法。

直接上程式碼:

寫法一:

改變域,變成區域性變數:

int main()

{

goto Exit;

{

int a = 0;

}

Exit:

return 0;

}

寫法二

神奇的寫法:

int main()

{

goto Exit;

int a;

a = 1;

Exit:

cout << "a = " << a << endl;

return 0;

}

關鍵是還可以訪問!結果:

[root@localhost c-c++]# g++ goto_

[root@localhost c-c++]# ./

a = 1259648

  研究

神奇的寫法

看到兩個可以編譯通過的寫法之後,最納悶的.是寫法二為毛可以編譯通過,而且還能使用???

C++規定

參考[1][2]中提到了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.

意思是說:如果一個程式的執行路徑從程式碼中的點A(某個區域性變數x還未定義)跳到程式碼中另一點B(該區域性變數x已定義,並且定義的時候有初始化),那麼編譯器會報錯。這樣的跳躍可以是由於執行goto語句,或者是switch-case造成的。所以,在寫法二中a是int型別,是一個POD型別,並且沒有初始化,所以編譯通過。但是,很明顯:如果去使用這個變數a的時候,結果是未知的,就像前輩說的,沒有意義,還不如不支援!那如果只在區域性使用,完全可以用花括號括起來!網上也有人說到,C++規範雖然沒有明確說明這樣是錯誤的,但是變數的域的規定其實是隱性說這種做法是不可取的,見參考[4]。

隱性說明

Goto can't skip over definitions of variables, because those variables would not exist after the jump, since lifetime of variable starts at the point of definition. The specification does not seem to explicitly mention goto must not do that, but it is implied in what is said about variable lifetime.

-fpermissive標記

參考[4]中提到,g++編譯器預設是檢查的,自己可以設定編譯器的這個標記變成警告,未實踐!!!

查了下資料-fpermissive標記的作用是: 把程式碼的語法錯誤作為警告,並繼續編譯程序,所以就安全起見,這個角度就不要想了,還是老老實實碼磚!

POD型別

參考[3],按照上面C++規定的說法,只要是POD型別,並且沒有初始化都是可以編譯通過的。

看一段程式碼:

#include <iostream>

using namespace std;

class A{

public:

// 注意:和B不同的是有構造和解構函式, 所以編譯報錯

A(){}

~A(){}

void testA(){

cout << "A::test." << endl;

}

};

class B{

public:

void testB(){

cout << "B::test." << endl;

}

};

int main()

{

goto Exit;

// int a = 1; // windows x failed!

//A classA; // failed:

B classB; // success:

B();

Exit:

B();

return 0;

}

結果:

[root@localhost c-c++]# g++ goto_

[root@localhost c-c++]# ./

a = 1259648

B::test.

小結

1、以上程式碼在windows和linux下均編譯通過和執行;

2、A classA一句在windows和linux均編譯不通過!因為A有構造和解構函式,不滿足條件;

3、至於int a = 1;這樣的寫法在windows(msvc)下面能夠通過就與C++規範不符了,求解釋!!!

以下是POD型別(還是看英文吧):

1、int, char, wchar_t, bool, float, double是POD型別,這些型別的long/short and signed/unsigned版本也是;

2、 指標(包括函式指標和成員指標)都是POD型別;

3、enums列舉型別;

4、POD的const和普通變數也都是;

5、POD型別的class,struct和union也是。但要求所有的成員是public,並且沒有基類,沒有構造、解構函式和虛擬函式。靜態成員在這些規則下也是。

  總結

1、最好不要用goto;

2、goto後面不要跳過定義和初始化的變數,如果是POD型別可以先申明再定義,是不會編譯報錯的。但是不建議這麼使用,可以看到如果執行語句跳過了賦值語句,那麼變數的值是未知的,存在危險性;

3、goto後面如果是區域性的變數,可以用花括號括起來構成一個區域性域,就安全了。

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。

  參考

[1]Getting a bunch of crosses initialization error

[2]>switch case、goto對變數定義的影響

[3]>“POD type” in C++

[4]>Statement goto can not cross pointer definition?

[5]>error: jump to label ‘foo' crosses initialization of ‘bar'