簡體   English   中英

將 sizeof() 的結果賦值給 ssize_t

[英]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_tsize_t之間的任何關系。

相關問題:

應該使用什么類型來循環數組?

不能保證SSIZE_MAX >= SIZE_MAX 事實上,情況不太可能是這樣,因為size_tssize_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 通常不需要接口來保證處理所有數據。 例如, readwrite接受一個size_t ,它描述了要讀/寫的緩沖區的長度,並返回一個ssize_t ,它描述了實際讀/寫的字節數; 這意味着即使有更多數據可用,也不會讀取/寫入超過SSIZE_MAX個字節。 但是,Posix 的基本原理還指出,特定實現可能會提供允許處理更大塊的擴展(“如果實現提供了擴展范圍,則使用擴展的符合應用程序將能夠使用整個范圍”),其想法是例如,實現可以指定通過將它們轉換為size_t來解釋 -1 以外的返回值。 這樣的擴展是不可移植的; 在實踐中,大多數實現確實將可以在單個調用中處理的字節數限制為可以在ssize_t報告的ssize_t

  • ptrdiff_t是(在標准 C 中)兩個指針之間差異的結果的類型。 為了明確定義指針減法,兩個指針必須指向同一個對象,要么指向對象,要么指向緊跟在對象后面的字節。 C 委員會認識到,如果ptrdiff_tsize_t的有符號等價物,那么兩個指針之間的差異可能無法表示,導致未定義的行為,但他們更願意要求ptrdiff_t是比size_t更大的類型。 你可以反對這個決定——很多人都有——但它自 C90 以來一直存在,現在似乎不太可能改變。 (來自 §6.5.6/9 的當前標准措辭:“如果結果在該類型的對象中無法表示 [ ptrdiff_t ],則行為未定義。”)

    與 Posix 一樣,C 標准沒有定義未定義的行為,因此將其解釋為禁止在非常大的對象中減去兩個指針是錯誤的。 始終允許實現定義標准未定義的行為的結果,因此實現指定如果PQ是指向同一對象的兩個指針(其中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 )的唯一方法是,如果PQ所指的對象的大小為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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM