簡體   English   中英

為什么 snprintf() 采用 size_t 大小限制,但返回打印的字符數為 int?

[英]Why does snprintf() take a size_t size limit, but returns an int number of chars printed?

令人尊敬的snprintf() function...

int snprintf( char *restrict buffer, size_t bufsz, const char *restrict format, ... );
  • 返回它打印的字符數,或者更確切地說,如果沒有緩沖區大小限制,它會打印的字符數。
  • 以字符/字節為單位獲取緩沖區的大小。

緩沖區大小為size_t但返回類型僅為int有何意義?

如果snprintf()應該能夠將超過INT_MAX字符打印到緩沖區中,那么它肯定必須返回一個ssize_t或一個size_t with (size_t) - 1表示錯誤,對吧?

如果它不能打印超過INT_MAX字符,為什么bufszsize_t而不是unsignedint 或者 - 是否至少正式限制持有不大於INT_MAX的值?

printf早於size_t和類似的“便攜式”類型的存在——當printf首次標准化時, sizeof的結果是int

這也是為什么在printf參數列表中讀取的*寬度或精度格式中的參數是int而不是size_t的原因。

snprintf更新,因此它作為參數的大小被定義為size_t ,但返回值被保留為int以使其與printfsprintf相同。

請注意,您可以使用這些函數打印超過INT_MAX字符,但如果這樣做,則返回值未指定。 在大多數平台上, intsize_t都將以相同的方式返回(在主返回值寄存器中),只是size_t值可能超出了int的范圍。 如此多的平台實際上從所有這些例程返回一個size_t (或ssize_t )並且超出范圍的事情通常會正常工作,即使標准不需要它。

大小和回報之間的差異已在線程https://www.austingroupbugs.net/view.php?id=761中的標准組中進行了討論。 這是該線程末尾發布的結論:

進一步的研究表明,當返回值溢出 int 時的行為在 C99 中被 WG14 澄清,將其添加到附件 J 的未定義行為列表中。它在 C11 中更新為以下文本:

“J.2 未定義行為 在以下情況下行為未定義:[跳過] — 格式化 output function(或寫入數組,或本應寫入數組)傳輸的字符數或寬字符數是大於 INT_MAX(7.21.6.1、7.29.2.1)。”

請注意,此描述提及 snprintf 的大小參數或緩沖區的大小。

如果snprintf()應該能夠將超過INT_MAX字符打印到緩沖區中,那么它肯定必須返回一個ssize_t或一個size_t ,其中(size_t) - 1表示錯誤,對吧?

不完全的。

C 也有fprintf()和朋友的環境限制

任何一次轉換可以產生的字符數至少應為 4095。” C17dr § 7.21.6.1 15

任何超過 4095 per %的風險都存在可移植性,因此int ,即使是 16 位 ( INT_MAX = 32767 ),也足以滿足可移植代碼的大多數目的。

注意: ssize_t不是 C 規范的一部分。

緩沖區大小為size_t但返回類型僅為 int 有何意義?

官方的 C99 基本原理文檔沒有討論這些特殊考慮,但可能是出於一致性和(單獨的)意識形態原因:

  • 所有printf系列函數都返回一個意義基本相同的int 這是在size_t被發明之前定義好的(對於原始的printffprintfsprintf )。

  • 在某種意義上, size_t類型是傳送尺寸和長度的正確類型,因此當在 C99 中引入它們(連同size_t本身)時,它被用於第二個 arguments 到snprintfvsnprintf

如果snprintf()應該能夠將超過INT_MAX字符打印到緩沖區中,那么它肯定必須返回一個ssize_t或一個size_t ,其中(size_t) - 1表示錯誤,對吧?

那將是一個更加內部一致的設計選擇,但不是。 似乎選擇了 function 系列的一致性。 請注意,該系列中的任何函數都沒有記錄對它們可以 output 的字符數的限制,並且它們的一般規范意味着沒有固有的限制。 因此,它們都面臨同樣的問題,即輸出很長。

如果它不應該打印超過INT_MAX字符,為什么 bufsz 是size_t而不是unsignedint 或者 - 是否至少官方限制持有不大於INT_MAX的值?

對於bufsz參數的值沒有記錄的約束,除了它必須可以表示為size_t的隱含約束。 甚至在最新版本的標准中也沒有。 但請注意,也沒有任何內容表明類型int不能表示size_t可表示的所有值(盡管在大多數實現中確實不能表示)。

所以是的,當非常大的數據是 output 通過這些函數時,實現將無法按照規范執行,其中“非常大”取決於實現。 因此,實際上,不應依賴使用它們在一次調用中發出非常大的輸出(除非打算忽略返回值)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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