函数strncpy()
并不总是null终止所以我想知道什么是总是null终止的最佳替代方案?
我想要一个函数,如果:
strlen(src) >= n /*n is the number of characters to be copied from source*/
没有必要再添加这样的代码:
buf[sizeof(buf)-1] = 0;
答案 0 :(得分:6)
如果您想要复制的字符串长度未知,可以在此处使用snprintf
。此函数将格式化输出发送到 str 。它与sprintf()
类似,但不会写入由 str 分配的更多字节。如果生成的字符串长于n-1
个字符,则剩余的字符将被忽略。除非缓冲区大小为\0
,否则它还始终包含空终止符0
。
如果你真的不想使用它,这将是strncpy()
或strcpy()
的替代品。但是,使用strcpy()
在字符串末尾手动添加空终止符始终是一种简单有效的方法。在C中,在任何已处理的字符串的末尾添加空终止符是非常正常的。
以下是使用sprintf()
的基本示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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()
仅适用于空终止字符串,如果字符串未终止,则会导致undefined behaviour。 snprintf()
还需要更多错误检查,可以在man page上找到。
正如其他人所说,这不是一种有效的方法,但如果你去寻找它就会存在。
答案 1 :(得分:3)
作为建议snprintf()
的{{3}}的替代方法:(注意: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
不必是字符串,只是一个字符数组。
字符串的另一种工具。
答案 2 :(得分:2)
使用strlcpy()
功能。 strlcpy()
获取目标缓冲区的完整大小,并保证在有空间时终止NULL。阅读man页面了解更多信息。
答案 3 :(得分:2)
如果你想要的行为是strcpy
的截断版本,它将源字符串的最长初始前缀复制到已知大小的缓冲区中,那么有多种选择:
你可以编写一个量身定制的功能来完成这项任务:
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()
并强制终止空值。如果目标数组很大,这将是低效的,因为如果源字符串较短,strncpy()
也填充目标数组的其余部分将为空字节。这个函数的语义非常反直觉,很难理解,容易出错。即使正确使用,strncpy()
的出现也是等待发生的错误,因为下一个程序员,更大胆但不太精明,可能会改变代码并引入它们以尝试优化他不理解的代码。安全地玩:避免这个功能。
答案 4 :(得分:1)
作为说明,必须使用strncpy()
的替代方法,请考虑Git 2.19(Q3 2018),它发现滥用诸如strcat()之类的系统API函数太容易了; strncpy()
; ...这些选定的函数现在在此代码库中被禁止,并且会导致编译失败。
该补丁程序确实列出了几种选择,这使其与此问题相关。
请参见commit e488b7a的commit cc8fdae,commit 1b11b64,commit c8af66a和Jeff King (peff
)(2018年7月26日)。
(由Junio C Hamano -- gitster
--在commit e28daf2中合并,2018年8月15日)
banned.h
:将strncpy()
标记为禁止
strncpy()
函数比strcpy()
可怕,但是由于其有趣的终止语义仍然很容易被误用。
即,如果它被截断,它将忽略NUL终止符,并且您必须记住自己添加它。即使您正确使用它,有时读者也很难在不遍历代码的情况下进行验证。
如果您正在考虑使用它,请考虑:
strlcpy()
,如果您确实只需要一个截断但NUL终止的字符串(我们提供了一个兼容版本,因此它始终可用)xsnprintf()
,如果您确定要复制的内容合适strbuf
或xstrfmt()
,如果您需要处理任意长度的堆分配字符串。请注意,
strncpy
中有一个compat/regex/regcomp.c
实例,很好(它在复制前分配了足够大的字符串)。
但这即使使用NO_REGEX=1
进行编译也不会触发禁止列表,因为:
- 我们在编译时不使用git-compat-util.h(相反,我们依赖上游库中的系统包含);和
- 它在一个“
#ifdef DEBUG
”块中由于它不会触发
banned.h
代码,因此我们最好保留它,以使与上游的差异最小。
注意:一年多以后,在Git 2.21(2019年第一季度)中,“ strncat()
”功能本身现在也 被禁止了。
请参见commit ace5707的Eric Wong (ele828
)(2019年1月2日)。
(由Junio C Hamano -- gitster
--在commit 81bf66b中合并,2019年1月18日)
banned.h
:将strncat()
标记为禁止
strncat()
具有与strcat()
相同的二次行为,并且难以阅读且容易出错。尽管在Git iself中这还不是问题,但strncat()
发现它已进入master
的'cgit
'中,并在我的系统上造成了段错误。
答案 5 :(得分:-1)
strcpy
函数始终为null终止。当然,您应该包含防止缓冲区溢出的代码,例如:
char buf[50];
if (strlen(src) >= sizeof buf)
{
// do something else...
}
else
strcpy(buf, src);