2012-02-07 49 views
7

考慮下面的代碼:爲什麼這兩個指針減法給出不同的結果?

char* p = new char[2]; 
long* pi = (long*) p; 
assert(p == pi);   // OK 

char* p1 = &p[1]; 
long* pi1 = (long*) p1; 
assert(p1 == pi1);  // OK 

int d = p1 - p; 
int d1 = pi1 - pi; 
assert(d == d1);   // No :(

在此之後運行,我得到d == 1d1 == 0,雖然p1 == pi1p == pi(我查了一下這個在調試器)。這是不確定的行爲?

+1

如果你看看反彙編,'pi1-pi'產生一個減法,然後右移2位。 (在MSVC上)這種右移很明顯是由'sizeof(long)'劃分的,當然這隻會被截斷爲0,只有1的差異。不管這個行爲是否定義,我都不知道。 – Mysticial 2012-02-07 22:57:40

回答

10

正如其他人所指出的,這是未定義的行爲。然而,對於你所看到的,有一個非常簡單的解釋。

指針之間的差異是元素的數量,而不是它們之間的字節數。

pi和pi1都指向多頭,但pi1指向的地址只比pi多一個字節。假定長度爲4個字節,地址差異1除以元素4的大小爲0.

另一種考慮這種方式的方式是你可以想象編譯器會生成等同於此的代碼用於計算d1:

int d1 = ((BYTE*)pi1 - (BYTE*)pi)/sizeof(long). 
5

如果指針不指向相同的數組,或者指針是從指向不相關類型的指針進行類型轉換,那麼兩個指針之間的區別是不確定的。

此外,差異不是以字節爲單位,而是以元素數量爲單位。

在第二種情況下,差異是1個字節,但它被除以sizeof(long)。請注意,因爲這是未定義的行爲,所以這裏絕對的任何答案都是正確的。

+0

男人,我笑我的背後看着你的頭像:)這是一個絕妙的主意:))) – 2012-02-07 22:52:59

+2

它是什麼部分說,你不能做指針從不相關的類型鑄造指針算法 – 2012-02-07 23:00:38

+0

@ SethCarnegie,我不能指出任何具體的東西,事實上我可能會誤解。我認爲類型轉換本身生成了UB,尤其是在這種情況下,因爲它沒有對該類型進行對齊。 – 2012-02-07 23:09:35

2

重新解釋指針的基礎類型不會更改其地址。但是,根據指針類型,指針算術產生不同的結果。所以你在這裏描述的是完全正確的,這是我所期望的。請參閱pointer arithmetics

0

它做整數(長)指針算術pi1 - pi;

如果p1&p[4]您會看到它打印1d1,而差異實際上是4個字節。這是因爲sizeof (long) = 4個字節。

+0

'long'的大小與平臺有關,因此即使'p1'指向'&p [4 ](或者可能是「4」或其他)。事實上,許多64位平臺都有8byte'long'。 IIRC甚至存在'sizeof(long)== 1'(這些'char'例如是'64bit')的平臺, – Grizzly 2012-02-07 22:59:43

相關問題