c语言 - 为什么 snprintf() 采用size_t大小限制,但返回打印的整数字符数



古老的snprintf()函数...

int snprintf( char *restrict buffer, size_t bufsz, const char *restrict format, ... );
  • 返回它打印的字符数,或者更确切地说,如果没有缓冲区大小限制,它将打印的数字。
  • 采用缓冲区的大小(以字符/字节为单位)。

缓冲区大小size_t,但返回类型只是一个int,这有什么意义?

如果snprintf()应该能够在缓冲区中打印超过INT_MAX个字符,那么它肯定必须返回一个ssize_t或一个size_t(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 时的行为由 Wg14 在 C99 中通过将其添加到附录 J 中的未定义行为列表中来阐明。它在C11中更新为以下文本:

"J.2 未定义的行为 在以下情况下未定义该行为: [跳过] — 格式化输出函数传输的字符数或宽字符数(或写入数组,或本应写入数组的字符数)大于 INT_MAX (7.21.6.1、7.29.2.1)。

请注意,此描述没有提到 snprintf 的大小参数或缓冲区的大小。

缓冲区大小size_t,但返回类型只是一个整数有什么意义?

官方的C99理由文件没有讨论这些特定的考虑因素,但大概是出于一致性和(单独的)意识形态原因:

  • 所有printf系列函数都返回具有基本相同意义的int。 这早在size_t发明之前就已经定义(针对原始printffprintfsprintf)。

  • 从某种意义上说,类型size_t是传达大小和长度的正确类型,因此当在 C99 中引入这些参数(连同size_t本身)时,它被用于snprintfvsnprintf的第二个参数。

如果snprintf()应该能够在缓冲区中打印超过INT_MAX个字符,那么它肯定必须返回一个ssize_t或一个size_t(size_t) - 1指示错误,对吧?

这将是一个内部更一致的设计选择,但没有。 似乎选择了整个函数系列的一致性。 请注意,此系列中的任何函数都没有记录它们可以输出的字符数限制,并且它们的一般规范意味着没有固有的限制。因此,它们都面临着输出很长的相同问题。

如果它不应该能够打印超过INT_MAX个字符,为什么 bufsz 是size_t而不是unsignedint?或者 - 它是否至少被官方限制为持有不大于INT_MAX的值?

对第二个参数的值没有记录的约束,除了隐含的约束,即它必须可表示为size_t。 即使在最新版本的标准中也是如此。 但请注意,也没有任何内容表明类型int不能表示size_t可以表示的所有值(尽管实际上在大多数实现中它不能)。

所以是的,当通过这些函数输出非常大的数据时,实现将难以按照规范运行,其中"非常大"依赖于实现。 因此,作为一个实际问题,不应该依赖使用它们在单个调用中发出非常大的输出(除非打算忽略返回值)。

如果snprintf()应该能够在缓冲区中打印超过INT_MAX个字符,那么它肯定必须返回一个ssize_t或一个size_t(size_t) - 1指示错误,对吧?

差一点。

C 对fprintf()和朋友也有环境限制

任何一次转换可以产生的字符数应至少为 4095。 C17dr § 7.21.6.1 15

任何超过4095%都有可移植性的风险,因此int,即使是16位(INT_MAX = 32767),也足以满足大多数可移植代码的目的。

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

相关内容

  • 没有找到相关文章

最新更新