C语言 snprintf()总是以空值终止吗?

eqfvzcg8  于 2022-12-11  发布在  其他
关注(0)|答案(5)|浏览(267)

snprintf是否始终为空以终止目标缓冲区?
换句话说,这是否足够:

char dst[10];

snprintf(dst, sizeof (dst), "blah %s", somestr);

或者你必须这样做,如果一些字符串足够长?

char dst[10];

somestr[sizeof (dst) - 1] = '\0';
snprintf(dst, sizeof (dst) - 1, "blah %s", somestr);

我感兴趣的是标准说了什么,以及一些流行的libc可能会做什么,这不是标准的行为。

tp5buhyn

tp5buhyn1#

正如其他答案所表明的:它should
snprintf...将结果写入字符串缓冲区。(...)将以空字符终止,除非buf_size为零。
因此,您需要注意的是,不要将大小为零的缓冲区传递给它,因为(很明显)它不能将零写入“nowhere”。
然而,请注意微软的库 * 没有 * 一个名为snprintf的函数,而是历史上 * 只有 * 一个名为_snprintf的函数(注意前导下划线),该函数 * 不追加 * 终止空值。以下是文档(VS 2012,~~ VS 2013):
http://msdn.microsoft.com/en-us/library/2ts7cx93%28v=vs.110%29.aspx

传回值

设len为格式化数据字符串的长度(不包括终止空值)。_snprintf的len和count以字节为单位,_snwprintf的len和count以宽字符为单位。

  • 如果len〈count,则len字符存储在缓冲区中,附加空终止符,并返回len。
  • 如果len = count,则len字符存储在缓冲区中,不附加空终止符,并返回len
  • 如果len〉count,则count个字符存储在缓冲区中,不附加空终止符,并返回负值。

(...)
Visual Studio 2015(VC 14)显然引入了一致的snprintf函数,但是仍然存在带有前导下划线和 * 非 * 空终止行为的旧函数:

**snprintf**函数通过在buffer[count-1]处放置空终止符,在len大于或等于count时截断输出。(...)

对于除snprintf之外的所有函数,如果len = count,则len字符存储在缓冲区中,* 不追加空终止符 *,(...)

eulz3vhy

eulz3vhy2#

根据snprintf(3)联机帮助页。
函数snprintf()vsnprintf()最多将size字节(包括尾随空字节('\0'))写入str
所以,是的,如果size〉= 1,就不需要终止。

bweufnob

bweufnob3#

根据C标准,除非缓冲区大小为0,否则vsnprintf()snprintf() null终止其输出。
snprintf()函数应等同于sprintf(),但增加了n参数,该参数说明s引用的缓冲区大小。如果n为零,则不写入任何内容,并且s可以是空指针。否则,应丢弃第n-1个以外的输出字节,而不是将其写入数组。并且在实际写入阵列的字节的末尾写入空字节。
因此,如果你需要知道要分配多大的缓冲区,那么就使用大小为零的缓冲区,然后你可以使用一个空指针作为目的地。注意,我链接到了POSIX页面,但是这些页面明确地表明,在标准C和POSIX之间没有任何分歧,因为它们涵盖了相同的领域:
本参考页中描述的功能符合ISO C标准。此处描述的要求与ISO C标准之间的任何冲突都是无意的。本卷POSIX.1-2008遵循ISO C标准。
请注意Microsoft版本的vsnprintf()。当缓冲区中没有足够的空间时,它的行为肯定与标准C版本不同(它返回-1,而标准函数返回所需的长度)。Microsoft版本的null在错误条件下终止其输出,而标准C版本在错误条件下终止其输出,这一点并不完全清楚。
但是,请注意,自最初编写此答案以来,Microsoft已更改了规则(vsnprintf()):
从Visual Studio 2015和Windows 10中的UCRT开始,vsnprintf不再等同于_vsnprintfvsnprintf函数符合C99标准;保留_vnsprintf是为了与旧版Visual Studio程式码回溯相容。
类似的注解适用于snprintf()sprintf()
另请注意Do you use the TR 24731 safe functions?(有关vsprintf_s()的Microsoft版本,请参见MSDN)和Mac solution for the safe alternatives to unsafe C standard library functions?的答案

dfty9e19

dfty9e194#

一些旧版本的SunOS使用snprintf做了一些奇怪的事情,可能没有以NULL终止输出,并且返回值与其他人所做的不匹配,但是在过去10年中发布的任何东西都在做C99所说的事情。

omjgkv6w

omjgkv6w5#

C标准本身就有歧义。C99和C11对snprintf函数的描述完全相同。以下是C99的描述:

7.19.6.5 snprintf函数
概要

1个1 m2n1x

说明

2 snprintf函数等效于fprintf,不同之处在于输出被写入数组(由参数s指定)而不是流。如果n为零,则不写入任何内容,并且s可能是空指针。否则,将丢弃n-1 st以外的输出字符,而不是将其写入数组,并且在实际写入数组的字符的末尾写入一个空字符。如果复制发生在重叠的对象之间,则该行为未定义。

退货

3如果n足够大,snprintf函数会返回已经写入的字符数,不计算终止的空字符,如果发生编码错误,则返回负值。因此,当且仅当返回值为非负值且小于n时,空终止的输出才被完全写入。
"一方面“
否则,超出n-1 st的输出字符将被丢弃,而不是写入数组,并在实际写入数组的字符的末尾写入空字符

  • 如果(s指向一个3个字符长的数组,并且)n为3,则写入2个字符,并丢弃第二个字符以外的字符;则在这2个之后写入空字符(并且空字符将是写入的第三个字符)*。

我相信这回答了最初的问题。

答案:

如果在重叠的对象之间进行复制,则该行为未定义。
如果n为0,则不向输出写入任何内容
否则,如果没有遇到编码错误,* 则输出总是以空结尾 不管输出是否适合输出数组 *;如果不是,则丢弃一些字符以使得输出阵列从不溢出),
否则(如果遇到编码错误),输出 * 可以保持非空终止 *。
"另一方面"
最后一句
因此,当且仅当返回值为非负且小于n时,以空值终止的输出才被完全写入
给人 * 歧义 *(或者我的英语不够好)。我至少可以从两个方面来解释这个句子:
1.当且仅当返回值为非负且小于n时,输出为空终止(这意味着如果返回值 * 不 * 小于n,即输出(包括终止空字符)不适合数组,则输出 * 不是空终止 *)。
2.当且仅当返回值为非负且小于n时,输出为complete(未丢弃任何字符)。
我认为上面的解释1与答案相矛盾,会引起误解和冗长的讨论。这就是为什么描述snprintf函数的最后一句需要修改,以消除任何歧义(这为编写C语言标准建议提供了依据)。
我认为,明确措辞的例子可以从http://en.cppreference.com/w/c/io/fprintf(参见4))中得到,这要感谢@“Martin Ba”提供的链接。
另请参见问题“snprintf: Are there any C Standard Proposals/plans to change the description of this func?“。

相关问题