[英]Assign result of sizeof() to ssize_t
我碰巧需要將sizeof(x)
的結果與ssize_t
。
當然 GCC 給出了一個錯誤(幸運的是我(我使用了-Wall -Wextra -Werror
)),我決定做一個宏來獲得sizeof()
的簽名版本。
#define ssizeof (ssize_t)sizeof
然后我可以像這樣使用它:
for (ssize_t i = 0; i < ssizeof(x); i++)
問題是,我有任何保證SSIZE_MAX >= SIZE_MAX
嗎? 我想可悲的是,這永遠不會是真的。
或者至少是sizeof(ssize_t) == sizeof(size_t)
,這會減少一半的值,但仍然足夠接近。
我在 POSIX 文檔中沒有找到ssize_t
和size_t
之間的任何關系。
相關問題:
不能保證SSIZE_MAX >= SIZE_MAX
。 事實上,情況不太可能是這樣,因為size_t
和ssize_t
很可能是對應的無符號和有符號類型,所以(在所有實際架構上) SIZE_MAX > SSIZE_MAX
。 將無符號值轉換為不能保存該值的有符號類型是未定義行為。 所以從技術上講,你的宏是有問題的。
實際上,至少在 64 位平台上,如果要轉換為ssize_t
的值是實際存在的對象的大小,則不太可能遇到麻煩。 但是如果對象是理論上的(例如sizeof(char[3][1ULL<<62])
),您可能會得到一個令人不快的驚喜。
請注意,類型ssize_t
的唯一有效負值是 -1,這是一個錯誤指示。 您可能ssize_t
Posix 定義的ssize_t
與自 C99 以來在標准 C 中定義的ptrdiff_t
混淆。 這兩種類型在大多數平台上都是相同的,並且通常是對應於size_t
的有符號整數類型,但任一標准都不能保證這些行為中的任何一種。 但是,這兩種類型的語義是不同的,使用時應該注意這一點:
ssize_t
由許多 Posix 接口返回,以允許函數發出處理的字節數或錯誤指示的信號; 錯誤指示必須為 -1。 不期望任何可能的大小都適合ssize_t
; Posix 原理指出:
符合標准的應用程序將被限制為不能以大於
{SSIZE_MAX}
片段執行 I/O。
對於大多數返回ssize_t
的接口來說,這不是問題,因為 Posix 通常不需要接口來保證處理所有數據。 例如, read
和write
接受一個size_t
,它描述了要讀/寫的緩沖區的長度,並返回一個ssize_t
,它描述了實際讀/寫的字節數; 這意味着即使有更多數據可用,也不會讀取/寫入超過SSIZE_MAX
個字節。 但是,Posix 的基本原理還指出,特定實現可能會提供允許處理更大塊的擴展(“如果實現提供了擴展范圍,則使用擴展的符合應用程序將能夠使用整個范圍”),其想法是例如,實現可以指定通過將它們轉換為size_t
來解釋 -1 以外的返回值。 這樣的擴展是不可移植的; 在實踐中,大多數實現確實將可以在單個調用中處理的字節數限制為可以在ssize_t
報告的ssize_t
。
ptrdiff_t
是(在標准 C 中)兩個指針之間差異的結果的類型。 為了明確定義指針減法,兩個指針必須指向同一個對象,要么指向對象,要么指向緊跟在對象后面的字節。 C 委員會認識到,如果ptrdiff_t
是size_t
的有符號等價物,那么兩個指針之間的差異可能無法表示,導致未定義的行為,但他們更願意要求ptrdiff_t
是比size_t
更大的類型。 你可以反對這個決定——很多人都有——但它自 C90 以來一直存在,現在似乎不太可能改變。 (來自 §6.5.6/9 的當前標准措辭:“如果結果在該類型的對象中無法表示 [ ptrdiff_t
],則行為未定義。”)
與 Posix 一樣,C 標准沒有定義未定義的行為,因此將其解釋為禁止在非常大的對象中減去兩個指針是錯誤的。 始終允許實現定義標准未定義的行為的結果,因此實現指定如果P
和Q
是指向同一對象的兩個指針(其中P >= Q
,則(size_t)(P - Q)
是完全有效的(size_t)(P - Q)
是指針之間數學上正確的差異,即使減法溢出。 當然,依賴於這種擴展的代碼不會完全可移植,但如果擴展足夠常見,那可能不是問題。
作為最后一點,使用-1的模糊性既作為錯誤指示(在ssize_t
)和作為指針減法(在的可能的可澆注結果ptrdiff_t
)是不太可能是在實踐中本只要size_t
是一樣大的一個指針。 如果size_t
與指針一樣大,則PQ
的數學正確值可能是(size_t)(-1)
(又名SIZE_MAX
)的唯一方法是,如果P
和Q
所指的對象的大小為SIZE_MAX
,給定假設size_t
與指針的寬度相同,這意味着該對象加上隨后的字節占據了每個可能的指針值。 這與某些指針值 ( NULL
) 與任何有效地址不同的要求相矛盾,因此我們可以得出結論,對象的真正最大大小必須小於SIZE_MAX
。
請注意,您實際上無法做到這一點。
x86 Linux 中最大可能對象的大小略低於 0xB0000000,而SSIZE_T_MAX
為 0x7FFFFFFF。
我還沒有檢查read
和 stuff 實際上是否可以處理最大的對象,但如果可以的話,它的工作方式如下:
ssize_t result = read(fd, buf, count);
if (result != -1) {
size_t offset = (size_t) result;
/* handle success */
} else {
/* handle failure */
}
你可能會發現libc
被破壞了。 如果是這樣,如果內核良好,這將起作用:
ssize_t result = sys_read(fd, buf, count);
if (result >= 0 || result < -256) {
size_t offset = (size_t) result;
/* handle success */
} else {
errno = (int)-result;
/* handle failure */
}
我要把它當作一個 XY 問題。 您遇到的問題是您想將有符號數與無符號數進行比較。 而不是將sizeof
的結果轉換為ssize_t
,您應該檢查您的ssize_t
值是否小於零。 如果是,那么您知道它小於您的size_t
值。 如果沒有,那么您可以將其轉換為size_t
,然后進行比較。
例如,這里有一個比較函數,如果有符號數小於無符號數,則返回-1
,如果相等則返回 0,如果有符號數大於無符號數,則返回-1
:
int compare(ssize_t signed_number, size_t unsigned_number) {
int ret;
if (signed_number < 0 || (size_t) signed_number < unsigned_number) {
ret = -1;
}
else {
ret = (size_t) signed_number > unsigned_number;
}
return ret;
}
如果你想要的只是<
操作的等價物,你可以用這樣的方式更簡單一些:
(signed_number < 0 || (size_t) signed_number < unsigned_number))
如果signed_number
小於unsigned_number
並且它限制了分支開銷,那么該行將為您提供1
。 只需要一個額外的<
操作和一個logical-OR
。
ssize_t 是一種 POSIX 類型,它沒有被定義為 C 標准的一部分。 POSIX 定義 ssize_t 必須能夠處理區間 [-1, SSIZE_MAX] 內的數字,因此原則上它甚至不需要是正常的有符號類型。 這個有點奇怪的定義的原因是唯一使用 ssize_t 的地方是作為讀/寫/等的返回值。 職能。
實際上,它始終是與 size_t 大小相同的正常有符號類型。 但是如果你想對你的類型真正迂腐,除了處理 IO 系統調用的返回值之外,你不應該將它用於其他目的。 對於一般的“指針大小”有符號整數類型,C89 定義了 ptrdiff_t。 這在實踐中與 ssize_t 相同。
此外,如果您查看read()的官方規范,您會看到對於 'nbyte' 參數,它說'如果 nbyte 的值大於 {SSIZE_MAX},結果是實現定義的。'。 因此,即使 size_t 能夠表示比 SSIZE_MAX 更大的值,使用比 IO 系統調用更大的值也是實現定義的行為(如上所述,這是使用 ssize_t 的唯一地方)。 與 write() 等類似。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.