来自https://stackoverflow.com/a/13067917/156458
snprintf ...将结果写入字符串缓冲区。 (...)将以空字符终止,除非buf_size为零。
那么为什么Linux编程接口中的以下示例将字符串的最后一个字符(由snprintf()
分配)显式设置为\0
?
char *
inetAddressStr(const struct sockaddr *addr, socklen_t addrlen,
char *addrStr, int addrStrLen)
{
char host[NI_MAXHOST], service[NI_MAXSERV];
if (getnameinfo(addr, addrlen, host, NI_MAXHOST,
service, NI_MAXSERV, NI_NUMERICSERV) == 0)
snprintf(addrStr, addrStrLen, "(%s, %s)", host, service);
else
snprintf(addrStr, addrStrLen, "(?UNKNOWN?)");
addrStr[addrStrLen - 1] = '\0'; /* Ensure result is null-terminated */
return addrStr;
}
答案 0 :(得分:3)
如果'\0'
符合C标准,则将目标数组的最后一个字符显式设置为snprintf
似乎没用。
但是请注意,Linux内核使用自己的C库版本,该版本可能符合或可能不符合C标准。大多数功能都比最初指定snprintf
的C99标准要旧,这可以解释实现上的差异。内核snprintf
实现的当前版本确实设置了空终止符。发布的代码似乎来自用户代码,而不是内核代码,因此这无关紧要。
还请注意,某些C库具有非标准行为。例如,Microsoft C运行时库在发布后将近15年不支持C99扩展,并且由于各种原因仍不完全兼容。例如,snprintf
具有%n
转换的非标准行为。
还请注意,某些名称与标准函数非常相似的Microsoft扩展程序的行为令人惊讶且令人困惑。例如,如果输出被截断,_snprintf()
将不会在目标数组中设置空终止符。有关详细信息,请参见this documentation page。
对于发布的代码,C库不太可能来自Microsoft,但是linux内核可以在具有各种C库的系统中使用:GNU libc用于大多数桌面发行版,但是Android使用不同的库图书馆。代码片段的作者之一:
snprintf
写空终止符。答案 1 :(得分:1)
对于任何适当的snprintf
实现,都不需要这样做。但是,在某些系统上,snprintf
是一些“相似”功能的包装,它们在结束时做了诸如not writing a NUL byte之类的有趣事情,或者忽略了length
参数。来自任何POSIX系统的snprintf
或来自linux或freebsd内核的snprintf
都不是这种情况。
此外,该代码未检查snprintf
的返回值(是否能够复制整个字符串?),对于示例代码而言,这是很麻烦的。
snprintf
总是使用NUL字节终止复制到缓冲区的字符串,除非length / size参数为0 ,在这种情况下它不会复制任何内容。引用susv4 [1]:
int snprintf(char *restrict s, size_t n, const char *restrict format, ...);
snprintf()
函数应等效于sprintf()
,其中n
参数的添加,它指出缓冲区的大小 由s
引用。如果n
为零,则不写任何内容,s
可以 为空指针。否则,n-1
st之后的输出字节应为 丢弃而不是写入数组,并且空字节为 写在实际写入数组的字节的末尾。如果由于以下原因在重叠的对象之间进行复制 调用sprintf()或snprintf(),结果是不确定的。
[1] C99标准中的语言相同