2016-03-02 20 views
4

看起來像C/C++編譯器(clang,gcc等)產生與優化級別相關的不同的輸出。你也可以檢查這篇文章中包含的在線鏈接。C/C++不確定值:編譯器優化提供了不同的輸出(示例)

http://cpp.sh/5vrmv(將輸出從none更改爲-O3以查看差異)。

基於下面的一段代碼,可能有人解釋我有幾個問題:

#include <stdio.h> 
#include <stdlib.h> 

int main(void) { 

    int *p = (int *)malloc(sizeof(int)); 
    free(p); 
    int *q = (int *)malloc(sizeof(int)); 
    if (p == q) { 
     *p = 10; 
     *q = 14; 
     printf("%d", *p); 
    } 
    return 0; 
} 
  1. 是不是肯定的是,執行總是會進入if語句?我們如何知道p和q兩個指針的地址是相同的?
  2. 爲什麼不優化有輸出,而-O3有輸出爲相同的指示?
+3

至少在這裏'* p = 10;'你有未定義的行爲。不,不能保證釋放的地址被'malloc()'重用。 –

+0

對。好吧,好的。這是未定義的行爲,但爲什麼它在兩個不同的優化級別之間一致? – Dave5545

+4

_「但爲什麼它在兩個不同的優化級別之間一致?」_未定義的行爲意味着**任何事情都可能發生**,包括您的觀察。 –

回答

4
free(p); 

這接通的p內容到一個無效的指針值。

int *q = (int *)malloc(sizeof(int)); 

此行沒有爲p相關性。

if (p == q) { 

實現定義的行爲,因爲p有一個無效的指針值。

*p = 10; 

最後,這是不確定行爲,出於同樣的原因同上。

C++標準§3.7.4.2/ 4:

如果給出在標準 庫中釋放函數的參數是一個指向不屬於空指針值(4.10),則 釋放函數應該釋放由 指針引用的存儲,呈現無效所有指向 解除分配存儲的部分。通過一個無效指針值間接尋址並且將一個無效指針值傳遞給一個釋放函數有 未定義的行爲。任何其他使用無效指針值的實現定義的行爲都具有

因此,問題的答案是:

是不是肯定的是,執行總是會進入if語句?

這取決於實施。 C++語言不保證它。

爲什麼沒有優化有輸出14,而-O3輸出10爲相同的指令?

因爲取消引用無效指針時行爲未定義。


在C中,比較本身是未定義的行爲。附錄J.2 C標準中列出,其中所述行爲是未定義的情況下,並且列表包括:

的指針到一個對象,其壽命已經結束時,使用值。

您可能會發現以下問題,包括所有的意見和回答耐人尋味:Undefined, unspecified and implementation-defined behavior

+2

比較在C中是未定義的行爲。請參閱:https://stackoverflow.com/questions/26704344/why-does-misra-c-state-that-a-copy-of-pointers-可以導致一個內存異常/ 26704433#26704433 – 2501

+0

@ 2501只是爲了澄清:一個(呈現)無效指針有一個「不確定的值」,對吧?並且訪問一個不確定的值總會導致未定義的行爲? – Dave5545

+0

@ 2501:我爲C添加了一些東西。 –

0

確定執行將總是進入if語句嗎?我們如何知道p和q兩個指針的地址是相同的?

這是實現定義的,你不能依賴這種行爲。 pq確實可以相等,您已經釋放了由p指向的內存,因此q可能會獲得與p相同的地址。

爲什麼沒有優化有輸出14,而-O3輸出10爲相同的指令?

這是優化器是如何工作的,你可以在這裏看到您的版本:

https://goo.gl/yRfjIv

其中編譯器優化出14的分配,這裏的版本它看起來是正確的:

https://goo.gl/vSVV0E

正在分配值14,並且我只添加了一行p = q;

我不確定它爲什麼這樣工作,我會說編譯器假定你的代碼沒有未定義的行爲代碼,並在這樣的假設下進行優化。

[編輯]

未定義行爲是通過使用該編譯器假定不再有效指針值造成的,如果它後來就等於是一些新分配的內存塊也沒關係。合適的標準報價是由TartanLlama給出:

[basic.stc.dynamic.safety]

[注:使用無效指針值(包括將它傳遞給一個 解除分配功能)的效果是未定義,見3.7.4.2。即使非安全派生的指針值可能與 比較等於某個安全派生的指針值也是如此。末端音符]

+0

爲什麼編譯器會假定? – Dave5545

+1

@ Dave5545是因爲標準允許它這樣做,所以它可能使實現某些優化變得更容易。 – marcinj

+0

所以,我猜測,懸掛指針可以驗證語句,但在任何時候都會引起未定義的行爲。 – Dave5545

-3

if -condition可能是假的 - 這取決於具體的實施malloc()它可能會返回剛剛釋放的塊進行再利用或不同的一個。

但是,如果該程序打印什麼(因爲它有湊巧,q等於p),它必須打印14。一個編譯器生產的東西是越野車......

在這裏使用叮3.4.1和3.6.2我始終得到正確的答案,而gcc 4.2.1和5.3.0都會顯示錯誤。不幸的是,叮噹3.8.0也是如此。