strncpy()的最佳替代方法是什么?
函数strncpy()
并不总是null终止所以我想知道什么是始终为null终止的最佳替代? 我想要一个函数,如果:
strlen(src) >= n /*n is the number of characters to be copied from source*/
没有必要像这样添加更多代码:
buf[sizeof(buf)-1] = 0;
如果您想要复制的字符串的长度未知,则可以在此处使用snprintf
。 此函数将格式化输出发送到str 。 它与sprintf()
类似,但不会写入str分配的更多字节。 如果结果字符串长于n-1
字符,则剩余的字符将被省略。 它还始终包含空终止符\0
,除非缓冲区大小为0
。
如果你真的不想使用它,这将是strncpy()
或strcpy()
的替代品。 但是,使用strcpy()
在字符串末尾手动添加空终止符始终是一种简单有效的方法。 在C中,在任何已处理的字符串的末尾添加空终止符是非常正常的。
以下是使用sprintf()
的基本示例:
#include #include #include #define SIZE 1024 int main(void) { const size_t N = SIZE; char str[N]; const char *example = "Hello World"; snprintf(str, sizeof(str), "%s", example); printf("String = %s, Length = %zu\n", str, strlen(str)); return 0; }
打印出来:
String = Hello World, Length = 11
此示例显示snprintf()
将"Hello World"
复制到str
,并在末尾添加了\0
终止符。
注意: strlen()
仅适用于空终止字符串,如果字符串未终止,则会导致未定义的行为 。 snprintf()
还需要更多错误检查,可以在手册页上找到。
正如其他人所说的那样,这不是一种有效的方法,但如果你去寻找它就会存在。
作为建议snprintf()
的答案的替代方法:(注意:如果n <= 0
)
size_t sz = sizeof buf; /*n is the number of characters to be copied from source*/ int n = (int) sz - 1; snprintf(buf, sz, "%s", src);
代码可以使用以下精度 :
“......为
s
转换写入的最大字节数....”C11§7.21.6.14
sprintf(buf, "%.*s", n, src);
它有一个微妙的优点,因为src
不必是一个字符串 ,只是一个字符数组。
字符串的另一个工具。
使用strlcpy()
函数。 strlcpy()
获取目标缓冲区的完整大小,如果有空间,则保证NULL终止。 阅读手册页以获取更多信息。
如果您想要的行为是strcpy
的截断版本,它将源字符串的最长初始前缀复制到已知大小的缓冲区中,则有多个选项可供您使用:
-
你可以写一个量身定制的function来完成这项工作:
char *safe_strcpy(char *dest, size_t size, char *src) { if (size > 0) { size_t i; for (i = 0; i < size - 1 && src[i]; i++) { dest[i] = src[i]; } dest[i] = '\0'; } return dest; }
大多数BSD系统都有一个函数
strlcpy(char *dest, const char *src, size_t n);
运作相同。 它的参数顺序令人困惑,因为n
是dest
数组的大小,但是在src
参数之后。 -
你可以使用
strncat()
:char *safe_strcpy(char *dest, size_t size, char *src) { if (size > 0) { *dest = '\0'; strncat(dest, src, n - 1); } return dest; }
-
您可以使用
snprintf()
或sprintf()
,但感觉就像使用液压机打钉:snprintf(dest, size, "%s", src);
交替:
if (size > 0) { sprintf(dest, "%.*s", (int)(size - 1), src); }
-
您可以使用
strlen()
和memcpy()
,但只有在您知道源ptr指向空终止字符串时才可以使用。 如果源字符串比目标数组长得多,它的效率也低于上述两种解决方案:char *safe_strcpy(char *dest, size_t size, char *src) { if (size > 0) { size_t len = strlen(src); if (len >= size) len = size - 1; memcpy(dest, src, len); dest[len] = '\0'; } return dest; }
-
您可以使用
strncpy()
并强制null终止。 如果目标数组很大,这将是低效的,因为如果源字符串较短,strncpy()
也填充目标数组的其余部分将为空字节。 这个函数的语义非常反直觉,很难理解,容易出错。 即使正确使用,strncpy()
也是等待发生的错误,因为下一个程序员,更大胆但不太精明,可能会改变代码并引入它们以尝试优化他不理解的代码。 安全地玩:避免这个function。
为了说明必须使用strncpy()
的替代方法,请考虑Git 2.19(Q3 2018),它发现滥用系统API函数(例如strcat())太容易了。 strncpy()
; …这些选定的函数现在在此代码库中被禁止,并将导致编译失败。
该补丁确实列出了几个替代方案,这使得它与这个问题相关。
请参阅提交e488b7a , 提交cc8fdae , 提交1b11b64 (2018年7月24日),并提交c8af66a (2018年7月26日)作者: Jeff King( peff
) 。
(由Junio C gitster
合并- gitster
-在提交e28daf2 ,2018年8月15日)
banned.h
:将strncpy()
标记为禁止
strncpy()
函数比strcpy()
更糟糕,但由于其有趣的终止语义,它仍然很容易被滥用。
也就是说,如果它截断它省略了NUL终结符,你必须记住自己添加它。 即使您正确使用它,读者有时也很难在不通过代码的情况下validation这一点。
如果您正在考虑使用它,请考虑:
strlcpy()
如果你真的需要一个截断但NUL终止的字符串(我们提供一个compat版本,所以它总是可用的)xsnprintf()
如果您确定要复制的内容应该适合strbuf
或xstrfmt()
如果需要处理任意长度的堆分配字符串。请注意,
compat/regex/regcomp.c
中有一个strncpy
实例,这很好(它在复制之前分配了一个足够大的字符串)。
但即使用NO_REGEX=1
编译,这也不会触发禁止列表,因为:
- 编译时我们不使用git-compat-util.h(而是依赖于上游库中的系统包含); 和
- 它位于“
#ifdef DEBUG
”块中因为它不会触发
banned.h
代码,所以我们最好不要让它保持与上游最小的分歧。
strcpy
函数始终为null终止。 当然,您应该包含防止缓冲区溢出的代码,例如:
char buf[50]; if (strlen(src) >= sizeof buf) { // do something else... } else strcpy(buf, src);