C 为什么此示例将字符串的最后一个字符(由`snprintf()`指定)显式设置为`0`?
从 snprintf。。。将结果写入字符串缓冲区。(…)将以空字符结尾,除非buf_大小为零 那么,为什么下面的Linux编程接口示例明确地将字符串的最后一个字符(由C 为什么此示例将字符串的最后一个字符(由`snprintf()`指定)显式设置为`0`?,c,printf,C,Printf,从 snprintf。。。将结果写入字符串缓冲区。(…)将以空字符结尾,除非buf_大小为零 那么,为什么下面的Linux编程接口示例明确地将字符串的最后一个字符(由snprintf()指定)设置为\0 char * inetAddressStr(const struct sockaddr *addr, socklen_t addrlen, char *addrStr, int addrStrLen) { char host[NI_MAXHOST], ser
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;
}
这对于任何适当的
snprintf
实现都是不必要的。然而,在一些系统上,snprintf
是一些“类似”函数的包装器,它做了一些有趣的事情,比如在末尾或忽略length
参数。来自任何POSIX系统的snprintf
或来自或内核的snprintf
都不是这种情况
另外,该代码没有检查snprintf
的返回值(它能够复制整个字符串吗?),对于示例代码来说,这是非常糟糕的
snprintf
总是用NUL字节终止它复制到缓冲区的字符串,除非length/size参数为0,在这种情况下它不复制任何内容。引用[1]:
snprintf()
函数应等同于sprintf()
,具有
添加说明缓冲区大小的n
参数
由s
引用。如果n
为零,则不应写入任何内容,s
可以
必须是空指针。否则,超出n-1
st的输出字节应为
丢弃而不是写入数组,并返回空字节
在实际写入数组的字节末尾写入
如果复制发生在由于复制而重叠的对象之间
调用sprintf()或snprintf(),结果未定义
[1] C99标准中的语言是相同的如果
snprintf
符合C标准,则将目标数组的最后一个字符设置为'\0'
显然是无用的
但是请注意,Linux内核使用自己版本的C库,它可能符合也可能不符合C标准。大多数函数都比C99标准旧,C99标准首先指定了snprintf
,这可能解释了实现差异。内核的snprintf
实现的当前版本没有设置空终止符。发布的代码似乎来自用户代码,而不是内核代码,因此这与此无关
还请注意,一些C库具有非标准行为。例如,MicrosoftC运行时库在发布后的近15年内都不支持C99扩展,并且由于各种原因仍然不完全一致。例如,snprintf
对于%n
转换具有非标准行为
还请注意,一些Microsoft扩展的名称与标准函数非常相似,其行为方式令人惊讶和困惑。例如,如果输出被截断,则\u snprintf()
不会在目标数组中设置空终止符。有关血淋淋的详细信息,请参阅
对于发布的代码,C库不太可能来自微软,但linux内核可能用于具有各种C库的系统:GNU libc用于大多数桌面发行版,但Android使用不同的库。代码片段的作者可以是:
- 不完全理解C标准,编写了冗余代码
- 不信任本地C库的实现,不希望假定
写入空终止符snprintf
- 习惯性地使用防御性编程,并编写冗余代码以防万一
snprintf
不终止其缓冲区;请参阅我答案中的链接。@PizSelect:是,正确。代码要么来自旧内核,要么是很久以前编写的,没有更新。这是示例用户代码(来自一本书),而不是内核代码——请注意getnameinfo(3)的使用,如果我没有弄错的话,它将c99和snprintf()的日期后移。@pizdelect:确实,您是正确的。我想是防御性编程吧。
int snprintf(char *restrict s, size_t n, const char *restrict format, ...);