我现在在C中有以下代码
int length = 50
char *target_str = (char*) malloc(length);
char *source_str = read_string_from_somewhere() // read a string from somewhere
// with length, say 20
memcpy(target_str, source_str, length);
场景是target_str
初始化为50个字节。 source_str
是一串长度为20的字符串。
如果我想将source_str
复制到target_str
,我使用上面的memcpy(),长度为50,这是target_str
的大小。我在memcpy中使用length
的原因是,source_str
的最大值可能是length
但通常小于(在上面的例子中它是20)。
现在,如果我想基于其终止字符(source_str
)复制到'\0'
的长度,即使memcpy长度超过终止字符的索引,上面的代码是正确的方法吗?还是有其他建议。
谢谢你的帮助。
如果我想将source_str复制到target_str,请使用如上所述的memcpy(),其长度为50,即target_str的大小。我在memcpy中使用length的原因是,source_str可以具有最大长度值但通常小于该值(在上面的示例中为20)。
区分之间至关重要
source_str
指向的数组的大小,和source_str
指向的字符串长度(如果有的话)(+/-终结符)。如果source_str
肯定指向一个长度为50或更长的数组,那么你提出的memcpy()
方法是可以的。如果没有,那么当source_str
实际上指向较短的数组时,它会产生未定义的行为。可能会出现C实现的任何结果。
如果source_str
肯定指向一个(正确终止的)不超过length - 1
字符的C字符串,并且如果它是你要复制的字符串值,那么strcpy()
比memcpy()
更自然。它将复制所有字符串内容,包括终止符。当source_str
指向比length
短的数组时,这没有问题,只要它包含一个字符串终止符。
如果这两种情况都不确定,那么你不清楚你想做什么。 strncpy()
函数可能涵盖其中一些案例,但并不涵盖所有这些案例。
场景是target_str初始化为50个字节。 source_str是一个长度为20的字符串。
如果我想将source_str复制到target_str,请使用如上所述的memcpy(),其长度为50,即target_str的大小。
目前你要求memcpy在源字符串结束后读取30个字符,因为它不关心源上可能的null终止符,这是一个未定义的行为
因为你复制一个字符串,你可以使用strcpy而不是memcpy
但是大小的问题可以逆转,我的意思是目标可以小于源,没有保护你将再次有一个未定义的行为
因此你可以使用strncpy给出目标的长度,只需要在目标小于源的情况下添加最终的空字符:
int length = 50
char *target_str = (char*) malloc(length);
char *source_str = read_string_from_somewhere(); // length unknown
strncpy(target_str, source_str, length - 1); // -1 to let place for \0
target_str[length - 1] = 0; // force the presence of a null character at end in case
现在,如果我想基于其终止字符('\ 0')复制到source_str的长度,即使memcpy长度超过终止字符的索引,上面的代码是正确的方法吗?
没有;你将复制source_str
的整个内容,如果它发生在它所指向的字符串的已分配空间的末尾之前,则甚至超过空终止符。
如果您担心的是最小化程序使用的辅助空间,那么您可以使用strlen
来确定source_str
的长度,并根据它来分配target_str
。此外,strcpy
类似于memcpy
,但专门用于以null结尾的字符串(观察它没有“size”或“length”参数):
char *target_str = NULL;
char *source_str = read_string_from_somewhere();
size_t len = strlen(source_str);
target_str = malloc(len + 1);
strcpy(target_str, source_str);
// ...
free(target_str);
target_str = NULL;
memcpy用于复制固定的内存块,因此如果你想复制由'\n'
终止的更短的内容,你不想使用memcpy。
还有其他函数,如strncpy或strlcpy,它们可以执行类似的操作。最好检查实现的功能。为了便于阅读,我从原始源代码中删除了优化版本。
这是一个示例memcpy实现:https://git.musl-libc.org/cgit/musl/tree/src/string/memcpy.c
void *memcpy(void *restrict dest, const void *restrict src, size_t n)
{
unsigned char *d = dest;
const unsigned char *s = src;
for (; n; n--) *d++ = *s++;
return dest;
}
很明显,在这里,两段内存都被访问了n次。无论源或目标字符串的大小如何,如果它更短,都会导致将内存复制到字符串之外。这很糟糕,可能会导致各种不必要的行为。
这是来自:https://git.musl-libc.org/cgit/musl/tree/src/string/strlcpy.c的strlcpy
size_t strlcpy(char *d, const char *s, size_t n)
{
char *d0 = d;
size_t *wd;
if (!n--) goto finish;
for (; n && (*d=*s); n--, s++, d++);
*d = 0;
finish:
return d-d0 + strlen(s);
}
这里的诀窍是n && (*d = 0)
评估为false并将打破循环条件并提前退出。
因此,这给你想要的行为。