[英]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
字符,為什么bufsz
是size_t
而不是unsigned
或int
? 或者 - 是否至少正式限制持有不大於INT_MAX
的值?
printf
早於size_t
和類似的“便攜式”類型的存在——當printf
首次標准化時, sizeof
的結果是int
。
這也是為什么在printf
參數列表中讀取的*
寬度或精度格式中的參數是int
而不是size_t
的原因。
snprintf
更新,因此它作為參數的大小被定義為size_t
,但返回值被保留為int
以使其與printf
和sprintf
相同。
請注意,您可以使用這些函數打印超過INT_MAX
字符,但如果這樣做,則返回值未指定。 在大多數平台上, int
和size_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
被發明之前定義好的(對於原始的printf
、 fprintf
和sprintf
)。
在某種意義上, size_t
類型是傳送尺寸和長度的正確類型,因此當在 C99 中引入它們(連同size_t
本身)時,它被用於第二個 arguments 到snprintf
和vsnprintf
。
如果
snprintf()
應該能夠將超過INT_MAX
字符打印到緩沖區中,那么它肯定必須返回一個ssize_t
或一個size_t
,其中(size_t) - 1
表示錯誤,對吧?
那將是一個更加內部一致的設計選擇,但不是。 似乎選擇了 function 系列的一致性。 請注意,該系列中的任何函數都沒有記錄對它們可以 output 的字符數的限制,並且它們的一般規范意味着沒有固有的限制。 因此,它們都面臨同樣的問題,即輸出很長。
如果它不應該打印超過
INT_MAX
字符,為什么 bufsz 是size_t
而不是unsigned
或int
? 或者 - 是否至少官方限制持有不大於INT_MAX
的值?
對於bufsz
參數的值沒有記錄的約束,除了它必須可以表示為size_t
的隱含約束。 甚至在最新版本的標准中也沒有。 但請注意,也沒有任何內容表明類型int
不能表示size_t
可表示的所有值(盡管在大多數實現中確實不能表示)。
所以是的,當非常大的數據是 output 通過這些函數時,實現將無法按照規范執行,其中“非常大”取決於實現。 因此,實際上,不應依賴使用它們在一次調用中發出非常大的輸出(除非打算忽略返回值)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.