snprintf()总是null终止?

时间:2011-10-09 22:18:54

标签: c posix libc

snprintf是否始终为null,终止目标缓冲区?

换句话说,这是否足够:

char dst[10];

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

或者你必须这样做,如果somestr足够长吗?

char dst[10];

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

我对标准所说的内容以及一些流行的libc可能做的不是标准行为感兴趣。

5 个答案:

答案 0 :(得分:62)

其他答案确定:should

  

snprintf ...将结果写入字符串缓冲区。 (......)   将以null字符终止,除非buf_size为零。

所以你需要注意的是你不会向它传递一个零大小的缓冲区,因为(很明显)它不能写一个零到#34;无处不在"。


但是,要小心,Microsoft的库 没有一个名为snprintf的函数,而是历史记录只有有一个名为_snprintf的函数(注意前导下划线),其中不附加终止空值。这是文档(VS 2012,~~ VS 2013):

http://msdn.microsoft.com/en-us/library/2ts7cx93%28v=vs.110%29.aspx

  

返回值

     

设len为格式化数据字符串的长度(不包括   终止null)。 len和count是_snprintf的宽字节数   _snwprintf的字符。

     
      
  • 如果len< count,然后len个字符存储在缓冲区中,a   附加null-terminator,并返回len。

  •   
  • 如果len = count,则len个字符存储在缓冲区中, no   附加null-terminator,并返回len。

  •   
  • 如果len> count,则count字符存储在缓冲区中, no   附加null-terminator,并返回负值。

  •   
     

(...)

Visual Studio 2015(VC14)显然引入了符合snprintf函数,但具有前导下划线和空终止行为的遗留函数仍然存在:

  

当len更大时, snprintf 函数会截断输出   通过在null处放置一个null-terminator来计数   buffer[count-1]。 (...)

     

对于所有函数其他而不是snprintf,如果len = count,len   字符存储在缓冲区中,没有附加空终结符,   (...)

答案 1 :(得分:16)

根据snprintf(3)手册页。

  

函数snprintf()vsnprintf()最多将size个字节(包括尾随空字节('\ 0'))写入str

所以,是的,如果尺寸> = 1,则无需终止。

答案 2 :(得分:10)

根据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标准。

警惕微软版vsnprintf()。当缓冲区中没有足够的空间时,它与标准C版本的行为完全不同(它返回-1,标准函数返回所需的长度)。目前尚不完全清楚Microsoft版本null在错误条件下终止其输出,而标准C版本则终止。

还请注意Do you use the TR 24731 safe functions?的答案(有关vsprintf_s()的Microsoft版本的MSDN)和Mac solution for the safe alternatives to unsafe C standard library functions?

答案 3 :(得分:4)

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

答案 4 :(得分:4)

模糊性从C标准本身开始。 C99和C11都具有相同的snprintf功能描述。以下是C99的描述:

  

7.19.6.5 snprintf功能
  的概要
  1 #include <stdio.h> int snprintf(char * restrict s, size_t n, const char * restrict format, ...);
  的描述
  2 snprintf函数等效于fprintf,但输出被写入数组(由参数s指定)而不是流。如果n为零,则不写入任何内容,s可能是空指针。否则,输出超出n-1 st的字符   丢弃而不是写入数组,并在实际写入数组的字符末尾写入空字符。如果在重叠的对象之间进行复制,则行为未定义   的返回
  3 snprintf函数返回在n足够大的情况下写入的字符数,不计算终止空字符,如果发生编码错误则返回负值。因此,当且仅当返回值为非负且小于n时,才会完全写入以null结尾的输出。

一方面句子

  

否则,超出n-1 st的输出字符将被丢弃而不是写入数组,并且在实际写入的字符末尾写入空字符< / strong>进入数组

if(s指向3个字符长的数组,并且)n为3,则将写入2个字符,并丢弃第2个字符以外的字符;然后 null字符写在那些之后(并且空字符将是第3个字符写入)

我认为这回答了原来的问题 答案:
如果在重叠的对象之间进行复制,则行为未定义 如果n为0,则不会向输出写入任何内容 否则,如果没有遇到编码错误,输出总是以空值终止,无论输出是否适合输出数组;如果没有,则丢弃一些字符这样输出数组永远不会溢出),
否则(如果遇到编码错误)输出可以保持非空终止

另一方面
最后一句

  

因此,当且仅当返回值为非负且小于n

时,才会完全写入以null结尾的输出

给出歧义(或者我的英语不够好)。我可以用至少两种方式解释这句话:
1.当且仅当返回的值为非负且小于n 时,输出以null结尾(这意味着如果返回的值为 > 小于n,即输出(包括终止空字符)不适合数组,则输出不是以空值终止)。 /> 2.当且仅当返回的值为非负且小于n 时,输出完成(没有丢弃任何字符)。

我认为上述解释与答案相矛盾,引起误解和冗长的讨论。这就是为什么描述snprintf函数的最后一句需要改变才能消除任何歧义(这为将提案写入C语言标准提供了理由)。
我认为可以从http://en.cppreference.com/w/c/io/fprintf(见4))获取非模糊措辞的例子,感谢@&#34; Martin Ba&#34;为链接。

另请参阅问题&#34; snprintf: Are there any C Standard Proposals/plans to change the description of this func?&#34;。