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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7706936/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 09:51:53  来源:igfitidea点击:

Is snprintf() ALWAYS null terminating?

cposixlibc

提问by Prof. Falken contract breached

Is snprintf always null terminating the destination buffer?

snprintf 是否总是 null 终止目标缓冲区?

In other words, is this sufficient:

换句话说,这是否足够:

char dst[10];

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

or do you have to do like this, if somestr is long enough?

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

char dst[10];

somestr[sizeof (dst) - 1] = '##代码##';
snprintf(dst, sizeof (dst) - 1, "blah %s", somestr);

I am interested both in what the standard says and what some popular libc might do which is not standard behavior.

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

采纳答案by Martin Ba

As the other answers establish: It should:

正如其他答案所确立的那样:它应该

snprintf... Writes the results to a character string buffer. (...) will be terminated with a null character, unless buf_size is zero.

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

So all you have to take care is that you don't pass an zero-size buffer to it, because (obviously) it cannot write a zero to "nowhere".

因此,您需要注意的是不要将零大小的缓冲区传递给它,因为(显然)它不能将零写入“无处”。



However, bewarethat Microsoft's library does not havea function called snprintfbut insteadhistorically onlyhad a function called _snprintf(note leading underscore) which does not appenda terminating null. Here's the docs (VS 2012, ~~ VS 2013):

但是,请注意,Microsoft 的库没有调用函数snprintf,而是历史上只有一个函数调用_snprintf(注意前导下划线),它不附加终止空值。这是文档(VS 2012,~~ VS 2013):

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

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

Return Value

Let len be the length of the formatted data string (not including the terminating null). len and count are in bytes for _snprintf, wide characters for _snwprintf.

  • If len < count, then len characters are stored in buffer, a null-terminator is appended, and len is returned.

  • If len = count, then len characters are stored in buffer, no null-terminator is appended, and len is returned.

  • If len > count, then count characters are stored in buffer, no null-terminator is appended, and a negative value is returned.

(...)

返回值

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

  • 如果 len < count,则 len 个字符存储在缓冲区中,附加一个空终止符,并返回 len。

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

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

(……)

Visual Studio 2015(VC14) apparently introduced the conforming snprintffunction, but the legacy one with the leading underscore and the nonnull-terminating behavior is still there:

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

The snprintffunction truncates the output when len is greater than or equal to count, by placing a null-terminator at buffer[count-1]. (...)

For all functions otherthan snprintf, if len = count, len characters are stored in buffer, no null-terminator is appended, (...)

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

所有功能的其它snprintf,如果LEN =计数,len个字符被存储在缓冲器中,没有空终止符被附加,(...)

回答by piotr

According to snprintf(3) manpage.

根据 snprintf(3) 联机帮助页。

The functions snprintf()and vsnprintf()write at most sizebytes (including the trailing null byte ('\0')) to str.

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

So, yes, no need to terminate if size >= 1.

因此,是的,如果 size >= 1,则无需终止。

回答by Jonathan Leffler

According to the C standard, unless the buffer size is 0, vsnprintf()and snprintf()null terminates its output.

根据 C 标准,除非缓冲区大小为 0,vsnprintf()并且snprintf()null 终止其输出。

The snprintf()function shall be equivalent to sprintf(), with the addition of the n argument which states the size of the buffer referred to by s. If n is zero, nothing shall be written and s may be a null pointer. Otherwise, output bytes beyond the n-1st shall be discarded instead of being written to the array, and a null byte is written at the end of the bytes actually written into the array.

snprintf()函数应等效于sprintf(),并添加了 n 参数,该参数说明了 s 所引用的缓冲区的大小。如果 n 为零,则不应写入任何内容并且 s 可能是空指针。否则,超出 n-1st 的输出字节将被丢弃而不是写入数组,并且在实际写入数组的字节末尾写入空字节。

So, if you need to know how big a buffer to allocate, use a size of zero, and you can then use a null pointer as the destination. Note that I linked to the POSIX pages, but these explicitly say that there is not intended to be any divergence between Standard C and POSIX where they cover the same ground:

因此,如果您需要知道要分配多大的缓冲区,请使用大小为零,然后您可以使用空指针作为目标。请注意,我链接到 POSIX 页面,但这些页面明确表示标准 C 和 POSIX 之间没有任何分歧,因为它们涵盖了相同的领域:

The functionality described on this reference page is aligned with the ISO C standard. Any conflict between the requirements described here and the ISO C standard is unintentional. This volume of POSIX.1-2008 defers to the ISO C standard.

此参考页面上描述的功能符合 ISO C 标准。此处描述的要求与 ISO C 标准之间的任何冲突都是无意的。本 POSIX.1-2008 卷遵循 ISO C 标准。

Be wary of the Microsoft version of vsnprintf(). It definitely behaves differently from the standard C version when there is not enough space in the buffer (it returns -1 where the standard function returns the required length). It is not entirely clear that the Microsoft version null terminates its output under error conditions, whereas the standard C version does.

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

Note also the answers to Do you use the TR 24731 safe functions?(see MSDNfor the Microsoft version of the vsprintf_s()) and Mac solution for the safe alternatives to unsafe C standard library functions?

另请注意您是否使用 TR 24731 安全功能?(请参阅MSDN的 Microsoft 版本vsprintf_s())和Mac 解决方案,以获取不安全 C 标准库函数的安全替代方案?

回答by Art

Some older versions of SunOS did weird things with snprintf and might have not NUL-terminated the output and had return values that didn't match what everyone else was doing, but anything that has been released in the past 10 years have been doing what C99 says.

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

回答by Robin Kuzmin

The ambiguity starts from the C Standard itself. Both C99 and C11 have identical description of snprintffunction. Here is the description from C99:

歧义始于 C 标准本身。C99 和 C11 具有相同的snprintf功能描述。以下是来自 C99 的描述:

7.19.6.5 The snprintffunction
Synopsis
1 #include <stdio.h> int snprintf(char * restrict s, size_t n, const char * restrict format, ...);
Description
2 The snprintffunction is equivalent to fprintf, except that the output is written into an array (specified by argument s) rather than to a stream. If nis zero, nothing is written, and smay be a null pointer. Otherwise, output characters beyond the n-1st are discarded rather than being written to the array, and a null character is written at the end of the characters actually written into the array. If copying takes place between objects that overlap, the behavior is undefined.
Returns
3 The snprintffunction returns the number of characters that would have been written had nbeen sufficiently large, not counting the terminating null character, or a negative value if an encoding error occurred. Thus, the null-terminated output has been completely written if and only if the returned value is nonnegative and less than n.

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

On the one handthe sentence

一方面这句话

Otherwise, output characters beyond the n-1st are discardedrather than being written to the array, and a null character is written at the end of the characters actually writteninto the array

否则,st之外的输出字符将n-1被丢弃而不是写入数组,并在实际写入数组的字符末尾写入空字符

says that
if (the spoints to a 3-character-long array, and) nis 3, then 2 characters will be written, and the characters beyond the 2nd one are discarded; then the null character is written after those 2(and the null character will be the 3rd character written).

表示
如果(s指向 3 个字符长数组的点数,并且)n为 3,则写入 2 个字符,并丢弃第 2 个字符之后的字符;然后空字符写在那些 2 之后(空字符将是写的第 3 个字符)

And this I believe answers the original question.
THE ANSWER:
If copying takes place between objects that overlap, the behavior is undefined.
If nis 0 then nothing is written to the output
otherwise, if no encoding errors encountered, the output is ALWAYS null-terminated(regardless of whether the output fits in the output array or not; if not then some characters are discarded such that the output array is never overflown),
otherwise (if encoding errors are encountered) the output can stay non-null-terminated.

我相信这回答了最初的问题。
答案:
如果复制发生在重叠的对象之间,则行为未定义。
如果n是0,则没有数据被写入到输出
否则,如果没有遇到编码误差,输出总是空终止无论是否输出数组或不在输出配合;如果没有,那么一些字符被丢弃,使得输出数组永远不会溢出),
否则(如果遇到编码错误)输出可以保持非空终止

On the other hand
The last sentence

另一方面
最后一句

Thus, the null-terminated output has been completely written if and only if the returned value is nonnegative and less than n

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

gives ambiguity(or my English is not good enough). I can interpret this sentence in at least two ways:
1. The output is null-terminatedif and only if the returned value is nonnegative and less than n(which means that if the returned value is notless than n, i.e. the output (including the terminating null character) does not fit in the array, then the output is not null-terminated).
2. The output is complete(no characters have been discarded) if and only if the returned value is nonnegative and less than n.

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



I believe that the interpretation 1 above contradicts THE ANSWER, causes misunderstanding and lengthy discussions. That is why the last sentence describing the snprintffunction needs a change in order to remove any ambiguity (which gives grounds for writing a Proposal to the C language Standard).
The example of non-ambiguous wording I believe can be taken from http://en.cppreference.com/w/c/io/fprintf(see 4)), thanks to @"Martin Ba" for the link.

我认为上述解释 1 与答案相矛盾,会引起误解和冗长的讨论。这就是为什么描述snprintf函数的最后一句话需要更改以消除任何歧义(这为编写 C 语言标准提案提供了依据)。
我相信可以从http://en.cppreference.com/w/c/io/fprintf(参见参考资料4))中获取非歧义措辞的示例,感谢 @"Martin Ba" 提供的链接。

See also the question "snprintf: Are there any C Standard Proposals/plans to change the description of this func?".

另请参阅问题“ snprintf:是否有任何 C 标准提案/计划更改此函数的描述?”。