{
char *a, *b;
printf("%lx\n",(b-a));
}
通常工作,其實我無法想象它給予警告或者失敗的32位或64位計算機上。但是對於ANSI C和尺寸意識來說,這是正確的嗎?我希望這個代碼能夠在任何可能的平臺上工作,包括非Unix和嵌入式系統。char * a,* b;什麼類型(b-a),以及如何打印它?
{
char *a, *b;
printf("%lx\n",(b-a));
}
通常工作,其實我無法想象它給予警告或者失敗的32位或64位計算機上。但是對於ANSI C和尺寸意識來說,這是正確的嗎?我希望這個代碼能夠在任何可能的平臺上工作,包括非Unix和嵌入式系統。char * a,* b;什麼類型(b-a),以及如何打印它?
b - a
是一個ptrdiff_t
,您可以使用您的printf
格式打印%td
。從規範部分6.5.6 加法運算符:
當兩個指針相減,既應指向同一陣列對象,或一個過去的數組對象的最後一個元素的元素;結果是兩個數組元素的下標差異。結果的大小是實現定義的,其類型(帶符號的整數型)是
ptrdiff_t
在<stddef.h>
頭中定義。
printf
對於和相關的功能,部分7.19.6 格式的輸入/輸出功能:
t
指定一個以下d
,i
,o
,u
,x
,或X
轉換說明適用於ptrdiff_t
或相應的無符號整數類型參數;或一個以下n
轉換說明適用於一個指針指向一個ptrdiff_t
參數。
我在規範中一些更戳左右,而這似乎表明,兩個指針的差異可能甚至不適合在ptrdiff_t
,在這種情況下的行爲是不明確的:
J. 2未定義的行爲
- 減去兩個指針的結果在ptrdiff_t
(6.5.6)類型的對象中不可表示。
雖然我無法想象任何可能出現的實現。我想你可以檢查PTRDIFF_MIN
和PTRDIFF_MAX
在<stdint.h>
是非常確定的。
由於您尚未初始化變量a和b,代碼給出了未定義的行爲。但除此之外,類型B-A是ptrdiff_t型,這是大到足以容納結果。如果你有足夠的現代化C,你可以用%TX的printf它。
如果你不想使用%TX,你應該轉換結果所以實際上它會匹配(不只是偶然)的格式說明:
printf("%lx", (unsigned long)(a-b));
不難想象,一個系統可能有例如一個32位地址空間和一個32位ptrdiff_t,但64位長,然後你的printf將失敗。
不,size_t是無符號的。 – 2009-10-29 18:33:53
@尼古拉:謝謝。我一定喝醉了。 – 2009-10-29 18:41:11
我的C必須有多現代?我目前的C已經有了,但是我會碰到一個沒有它的C的機會是什麼? %tx何時添加到規範中? – 2009-10-30 00:45:49
這是ptrdiff_t
。從man stddef.h
:
ptrdiff_t Signed integer type of the result of subtracting two pointers.
與%td
打印。
b - a
的結果僅在a
和b
都指向相同字符數組的元素時才定義。這個要求也可以解釋爲指向屬於同一個對象的字節的a
和b
,因爲每個對象都可以被重新解釋爲一個字符數組。
否則,結果是未定義的。即試圖減去這樣的指針會導致未定義的行爲。
當定義結果時,它有ptrdiff_t
類型。 ptrdiff_t
是一個typedef名稱,隱藏在該typedef名稱後面的是執行定義的類型。該類型已知被簽名。
另請注意,即使指針指向相同數組的元素,C語言也不能保證ptrdiff_t
足夠大以容納任何減法的結果。如果指針與ptrdiff_t
類型相距太遠而不能適應結果,則行爲不確定。
有沒有具體的printf
格式說明爲ptrdiff_t
,即使在C99,所以你可能會更好將其轉換爲一個足夠大的有符號整數類型和使用的格式說明該類型
printf("%ld\n", (long) (b - a));
修正: C99確實有一個長度修飾符ptrdiff_t
。正確的方法來打印結果C99將
printf("%td\n", b - a);
注意t
是一個長度修飾符。它可以與d
,o
,u
,x
或X
轉換說明符結合使用,具體取決於您想要獲得的輸出格式。在C89/90中,您仍然需要堅持使用足夠大的簽名類型。
P.S.你說你無法想象它在32位或64位機器上失敗。事實上,很容易想象(或實際上使其失敗)。您看到32位機器上的ptrdiff_t
通常是32位類型。由於它是有符號類型,因此它只有31位可用來表示值的大小。如果你採用兩個分開的指針(即需要32位來表示「距離」),b - a
的結果會溢出並且沒有意義。爲了防止這種失敗,您需要在32位機器上至少有33位有符號ptrdiff_t
,並且在64位機器上至少有65位有符號ptrdiff_t
。實現通常不會這樣做,它們只是使用標準的「權限」在溢出上產生未定義的行爲。
什麼是「足夠大」?乍一看似乎「足夠大」,有沒有一種系統,指針不適合長時間,需要很長時間?很顯然,在32位系統中,長整型爲32位,所以足夠大,而在64位系統中長整型爲64位,所以足夠大。但我是否真的涵蓋了所有(ha!)系統? 感謝有關跡象的警告,我並不是很擔心這個問題的兩個指針確實指向了同一個數組,我無法想象這個數組會大於地址空間大小的一半。 – 2009-10-29 20:36:36
「足夠大」意味着:分析yor平臺(具體指針大小)並查看平臺上各種整型的大小。相應地選擇一個特定的整型。通常這將是一個有符號的整型,其大小與指針大小相同。 – AnT 2009-10-29 21:35:22
如果實現可以確保指向同一個數組的指針永遠不會超過2^31-1或2_63-1(即數組永遠不會超過這麼多元素),那麼溢出可能性就不成問題。基本上,所有的實現都必須確保不允許'malloc'尺寸大於'SIZE_MAX/2'。 – 2011-07-22 03:12:06
b-a的類型是ptrdiff_t
+1對於C標準的怪癖,如指針差異不適合在'ptrdiff_t'內(我也可以認爲比「怪癖」更難聽)。 – 2009-10-30 04:40:55
是的,事實證明'PTRDIFF_MIN' /'PTRDIFF_MAX'可能小至+/- 65536(根據規格)。我檢查過的實現並沒有像這樣瘋狂的東西 - 實際上這種奇怪的東西似乎不太可能出現。 – 2009-10-30 06:55:50
我會考慮一個實現,其中'ptrdiff_t'小於(比特的寬度),而不是最大可能的數組大小無法破解,甚至實現了* value位*數量少的實現(即大於SIZE_MAX/2元素的數組允許)相當危險...... – 2011-07-22 03:09:47