2013-05-26 34 views
2

我的問題是與C99/GNU-C和C++中的堆棧變量的生命週期有關,當goto通過它們時。這裏有一些相關的問題,但他們都沒有真正回答我的情況。考慮下面的代碼示例:POD堆棧變量的範圍和生命週期通過轉到它時

void Foo(char *ptr) 
{ 
label1: 
    if (ptr) 
    { 
     char string1[50]; 
     strcpy(string1, ptr); 
     strupr(string1); 
     printf("upcased string = %s\n", string1); 
     return; 
    } 

#if CASE_1 
    char string2[50] = "test"; 
#else 
    char string2[50]; 
    strcpy(string2, "test"); 
#endif 
    ptr = string2; 
    goto label1; 
} 

,我讀了一個跳轉不會引入新的作用域,因此變量應該是可訪問的(理論上)被宣佈之前就。 string2存在於函數範圍,但它不能直接從聲明之前的代碼訪問。另一方面,使用goto和指針變量可以訪問它。 我知道C++需要調用析構函數,當goto跨越對象初始化時,但我沒有發現內置/ POD類型的生命。 使用GCC進行的測試顯示,當編譯器重新使用堆棧空間時,ptr未分配給string2,當分配完成後,它將停止重用它,就好像它「知道」它可以在去。

在C99/C++標準(或者甚至可能僅限於GCC)中是否有任何規則明確表示,這是否允許?我對C++特別感興趣。

編輯:

  • 的C的一部分++標準,處理與它"3.7.3-1 Block-scope variables explicitly declared register or not explicitly declared static or extern have automatic storage duration. The storage for these entities lasts until the block in which they are created exits."雖然這似乎解釋了上面的代碼,它並沒有真正的,因爲很顯然,編譯器將重用堆棧自動變量的空間作爲優化,當它知道它不會再被使用時。所以需要回答的問題是:是否允許編譯器假定變量在聲明之前不在該位置使用,即使程序流將帶有引用?
  • 我添加了一個替代案例,它似乎有不同的規則。
  • 要回答任何問題,爲什麼我會首先使用這樣一個醜陋的構造:這當然不是我想要編寫普通代碼的方式。它被認爲是兼容的宏的一部分,以允許使用結構化異常以G處理++

回答

1

關於C++的,段落中的C++ 11標準的6.6/1指定:

[。 ..]從循環中移出,從塊中移出,或者通過 自動存儲持續時間涉及銷燬自動存儲持續時間在 範圍內的對象,但不在此點轉移到。 [...]

段落3.7.3/3,則指定:

如果具有自動存儲持續時間的變量初始化或具有副作用析構函數,不得 是在其塊結束之前銷燬,即使看起來 未被使用也不應作爲優化來消除,除非類對象或其複製/移動可以按照12.8中的規定取消。

由於string2已初始化,程序具有未定義的行爲。

這就是說,爲什麼在使用goto時只能使用結構化編程? Dijkstra taught us long ago that goto is harmful

+0

我可以擺脫初始化:'char string2 [50]; strcpy(string2,「test」);'因此在這種情況下 – Timo

+0

@Timo:在這種情況下,它是良構的,因爲3.7.3/1(「*這些實體的存儲一直持續到它們被創建的塊退出。*「)和3.8/1(*」T類型對象的生命期在以下情況下結束:如果T是具有非平凡析構函數的類類型(12.4),則析構函數調用開始;或者, 「* –

+0

關於第一個問題:」範圍從轉移點轉移到轉移點「,但考慮到範圍是函數,它不會超出範圍關於第二點:我可以去掉初始化:'char string2 [50]; strcpy(string2,「test」);'所以在這種情況下,第二部分也沒有定義。不適用,對不起,但我不會購買有害goto的教條,事實上它們可以使代碼更具可讀性(如在錯誤處理的情況下),但是對於請問你的問題爲什麼:因爲我可以;) – Timo

1

當行爲是在C++中未定義,因爲Andy Prowlanswer告訴你,在C,行爲被定義,第6段6.2.4(N1570,相同的如C99第5段)指定對象的生存期與不具有可變長度數組類型自動存儲持續時間:

對於這樣一個對象,該對象不具有可變長度數組類型,其壽命從進入與它直到的執行相關聯的塊延伸該塊以任何方式結束。 (輸入一個封閉的程序段或調用一個函數會暫停但不會結束當前程序段的執行。)如果程序段遞歸輸入,則每次創建一個新的對象實例。對象的初始值是不確定的。如果爲對象指定了初始化,則每次在執行塊時到達聲明或複合文字時執行該操作;否則,每次達到聲明時該值都變得不確定。

string2壽命是該塊的執行的整個時間,因此,使用ptr訪問它在if分支後的第一個初始化找到與確定的內容的對象。

+0

如果變量的地址是在跳回之前取得的,那麼變量是否會繼續保持它的值,直到達到聲明並在該確切點變得不確定,或者在此之前該值可能不確定? – supercat

+0

不確定你的意思。據我瞭解,由於我們不離開該區塊,所以跳躍時變量保持其值。 –

+0

引用語言明確指出,每次達到聲明時變量的值都變得不確定。我的問題是,這是否意味着編譯器在聲明之前不允許該值被破壞,但是聲明本身可以任意重寫該值(這看起來有點奇怪),或者它是否說了別的東西,或者可能意在表示別的意思,但沒有意思。 – supercat