我想知道是否有一种更干净、更有效的方法来完成以下strncpy
,考虑到max
数量的字符。我觉得做得太过分了。
int main(void)
{
char *string = "hello world foo!";
int max = 5;
char *str = malloc (max + 1);
if (str == NULL)
return 1;
if (string) {
int len = strlen (string);
if (len > max) {
strncpy (str, string, max);
str[max] = ' ';
} else {
strncpy (str, string, len);
str[len] = ' ';
}
printf("%sn", str);
}
return 0;
}
我根本不会为此使用strncpy
。至少如果我理解你想做什么,我可能会做这样的事情:
char *duplicate(char *input, size_t max_len) {
// compute the size of the result -- the lesser of the specified maximum
// and the length of the input string.
size_t len = min(max_len, strlen(input));
// allocate space for the result (including NUL terminator).
char *buffer = malloc(len+1);
if (buffer) {
// if the allocation succeeded, copy the specified number of
// characters to the destination.
memcpy(buffer, input, len);
// and NUL terminate the result.
buffer[len] = ' ';
}
// if we copied the string, return it; otherwise, return the null pointer
// to indicate failure.
return buffer;
}
首先,对于strncpy,"没有空字符隐式附加到目标的末尾,因此只有当源中C字符串的长度小于num时,目的地才会被null终止。
我们使用 memcpy((,因为 strncpy(( 在每个副本上检查每个字节是否有 0。我们已经知道字符串的长度,memcpy(( 做得更快。
首先计算字符串的长度,然后决定要分配和复制的内容
int max = 5; // No more than 5 characters
int len = strlen(string); // Get length of string
int to_allocate = (len > max ? max : len); // If len > max, it'll return max. If len <= max, it'll return len. So the variable will be bounded within 0...max, whichever is smaller
char *str = malloc(to_allocate + 1); // Only allocate as much as we need to
if (!str) { // handle bad allocation here }
memcpy(str,string,to_allocate); // We don't need any if's, just do the copy. memcpy is faster, since we already have done strlen() we don't need strncpy's overhead
str[to_allocate] = 0; // Make sure there's a null terminator
你正在重新发明1996年推出的strlcpy
- 参见Todd C. Miller和Theo de Raadt的strlcpy和strlcat-一致,安全,字符串副本和连接论文。你可能没有听说过它,因为它被拒绝添加到glibc中,被glibc维护者称为"非常低效的BSD废话",即使被所有其他操作系统采用,也一直战斗到今天 - 参见Damien Miller的安全可移植性论文(第4部分:选择正确的API(。
您可以使用libbsd项目(打包在Debian,Ubuntu和其他发行版上(在Linux上使用strlcpy,或者简单地复制在Web上容易找到的源代码(例如,在本答案中的两个链接上(。
但是回到你的问题,在你的情况下什么最有效,你在这里不使用源字符串长度是我的想法,基于OpenBSD的strlcpy
源,http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/strlcpy.c?rev=1.11 但没有检查原始字符串的长度,它可能很长,但仍然有正确的"\0"结尾:
char *d = str; // the destination in your example
const char *s = string; // the source in your example
size_t n = max; // the max length in your example
/* Copy as many bytes as will fit */
if (n != 0) {
while (--n != 0) {
if ((*d++ = *s++) == ' ')
break;
}
}
/* Not enough room in dst, add NUL */
if (n == 0) {
if (max != 0)
*d = ' '; /* NUL-terminate dst */
}
以下是使用 memcpy 的 strlcpy 版本 http://cantrip.org/strlcpy.c:
/*
* ANSI C version of strlcpy
* Based on the NetBSD strlcpy man page.
*
* Nathan Myers <ncm-nospam@cantrip.org>, 2003/06/03
* Placed in the public domain.
*/
#include <stdlib.h> /* for size_t */
size_t
strlcpy(char *dst, const char *src, size_t size)
{
const size_t len = strlen(src);
if (size != 0) {
memcpy(dst, src, (len > size - 1) ? size - 1 : len);
dst[size - 1] = 0;
}
return len;
}
我认为哪一个更有效取决于源字符串。对于很长的源字符串,strlen 可能需要很长时间,如果您不需要知道原始长度,那么第一个示例对您来说可能会更快。
这一切都取决于您的数据,因此对真实数据进行分析将是找出答案的唯一方法。
您可以通过以下方式减少代码量:
int main(void)
{
char *string = "hello world foo!";
int max = 5;
char *str = malloc(max + 1);
if (str == NULL)
return 1;
if (string) {
int len = strlen(string);
if (len > max)
len = max;
strncpy(str, string, len);
str[len] = ' ';
printf("%sn", str);
}
return 0;
}
您无能为力来进一步加快strncpy()
。 您可以通过使用以下方法减少时间:
char string[] = "hello world foo!";
然后改用sizeof(string)
来避免strlen()
。
请注意,如果最大大小很大并且要复制的字符串很小,那么strncpy()
在目标字符串中的每个未使用位置写入 null 这一事实确实会减慢速度。
strncpy()
命中 NUL,它将自动停止;通过max
而不检查就足够了。
这就足够了:
char *str = malloc(max+1);
if(! str)
return 1;
int len = strlen(string);
memset(str, 0, max+1);
int copy = len > max ? max : len;
strncpy(str, string, copy);