2017-04-12 69 views
0

在C++中,我完全意識到指針減法僅在數組內有效,下面的代碼是未定義的行爲。我知道嘗試推理未定義的行爲是毫無意義的,但我相信在詢問以下問題時有價值。2個指針,0字節差異但不等於

#include <cstddef> 
#include <iostream> 
#include <iomanip> 

int main() 
{ 
    long a = 1; 
    long b = 1; 

    std::cout << (char*)(&b) - (char*)(&a) << '\n'; //prints 8, ok we're 8 bytes apart 

    char* aPlus8 = (char*)&a + 8; //bump up 8 bytes 
    char* bPtr = (char*)&b; 

    std::cout << "diff in bytes = " << (bPtr - aPlus8) << '\n';   //prints 0. All good, looks like we're there 
    std::cout << "but are they the same? = " << (bPtr == aPlus8) << '\n'; //but are we ? 
} 

最後一行bPtr == aPlus8返回false,雖然字節差異是0.是否有可能的解釋? (除「因爲它是未定義的行爲」)

這是與g++ -std=c++14 -O3 -Wall -pedantic編譯。如果我改變優化級別,那麼預期的結果也會改變。

+0

C或C++?決定一個。您可以嘗試使用較少的優化級別編譯來檢查結果是否相同。 –

回答

6

最有可能的優化注意到bPtraPlus8不可能是平等的,所以它取代(bPtr == aPlus8)false節省一些CPU指令。

需要注意的是這種優化不只是節省CPU指令 - 想象一下,如果你有這樣的事情

if(bPtr == aPlus8) 
{ 
    // lots of complicated code here 
} 

那麼優化器將能夠去除if語句中的所有代碼。這就是爲什麼這個優化和類似的優點是有用的。

實際上,在現代編譯器中,未定義行爲的主要影響之一是它允許優化器找到簡化代碼的方法,這是您沒有想到的。

+1

除了徹底刪除代碼之外,此優化的另一個重要原因是指針別名迫使大量冗餘內存讀取。考慮將'* bPtr'加載到CPU寄存器的情況。如果編譯器知道'bPtr!= aPlus8',那麼它知道寄存器值不受'* aPlus8 = 42;'的影響。 – MSalters

0

C標準和可能的C++標準明確指出,如果兩個指針可以「合法地」訪問相同的對象,它們將比較相等,並且還清楚地表明指向一個對象的指針有時可以等於指針即使他們無法合法訪問同一個對象,也只是「過去」另一個對象。在大多數情況下,設計正確的代碼不應該在意指向一個對象的指針是否等於指針「剛剛過去」,因此我不認爲兩個標準是否清楚:

  1. 如果兩個對象是不相關的,那麼一個剛剛過去的指針與另一個指針之間的每個單獨比較應該被認爲是產生獨立的未指定結果(意味着指針相等運算符不需要表現爲等價關係)。

  2. 可以通過「直接」和「剛過去」指針連接的一組不相關對象是未指定的,但涉及這樣的指針的比較將產生與某些這樣的集合一致的結果(意味着指針相等運算符將有效定義一個等價關係)。

  3. 指針相等的語義以比#1更精確的方式定義,但比#2更精確。

標準故意要求所有實現適合於系統編程事不聞不問,如果他們允許進行包括指向不相關的對象都比較報告說,他們是不相等的編譯器可以生成更高效的代碼。如果沒有標準通常要求的某些語義保證,系統編程等目的的代碼可以指定它不起作用,並且任何不能履行這些保證的實現都必須拒絕彙編。我不知道C或C++中的任何方式來指定代碼需要指針相等運算符表現爲等價關係,但C++還有其他方法來比較指針,如std::less,它們提供了比運算符更強的保證。