使用printf打印非零结尾字符串时,精度的可移植性问题

5

正如这里的多个问题所指出的那样,您可以通过将精度格式化为要打印的最大长度来printf非终止字符串。例如:

printf("%.*s\n", length, str);

会打印从 str 开始的 length 个字符(或直到第一个0个字节)。

正如jonathan-leffler在这里指出的那样,这是由posix规定的。而且在阅读文档时,我发现它实际上从未说明这应该起作用(或者我找不到),“‘%s’转换打印字符串。 ”和“字符串是空字符结尾的字节数组[...] ”。关于精度的方面说明了“可以指定精度以指示要写入的最大字符数;”。

我的解释是上述行为实际上是未定义行为,但因为printf的实现效率高,它不会读取多于写入的内容。

所以我的问题是:这个解释是正确的吗?

TLDR:当我尝试posix兼容时,是否应该停止使用此printf技巧,因为存在可能导致缓冲区溢出的实现?


我认为这更像是一种疏忽而不是“有意”的未定义行为。但严格来说,可以想象出一种会调用UB的实现方式。 - Eugene Sh.
如果您知道数据不是以空字符结尾的,则可以使用fwrite而无需进行任何其他逻辑。 - William Pursell
假设您将最大长度指定为8,但未终止的数组仅具有4个有效字符?希望不会发生溢出似乎有点危险。 - Weather Vane
@WeatherVane 这不是与前面提到的 fwrite 同等水平 吗? - Eugene Sh.
@EugeneSh。也许吧,但二进制文件写入语句通常关注源大小,而文本限制通常关注输出约束。 - Weather Vane
1个回答

5
你现在阅读的不是POSIX规范,而是GNU libc手册,为了易读性会比较简略。实际的规范可以在https://pubs.opengroup.org/onlinepubs/9699919799/functions/printf.html找到(它甚至从你链接的Jonathan Leffler的答案中链接到),它明确指出你的代码没问题:
“s”参数应该是一个指向char数组的指针,数组中的字节将被写入,直到(但不包括)任何终止空字节。如果指定了精度,则最多只能写入那么多个字节。如果没有指定精度或精度大于数组的大小,则应用程序应确保数组包含空字节。
需要注意的是,他们小心地没有使用“字符串”这个词,正如你指出的原因一样。
ISO C17标准使用几乎相同的语言,因此你的代码甚至可以移植到非POSIX标准的C实现。(POSIX通常包含ISO C,并且许多POSIX规范的部分都是复制/粘贴自C标准。)

谢谢,我认为它们除了(null)部分之外是相同的。 - Poohl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接