我现在在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 length大于终止字符的索引,上述代码是否正确?办法吗?还是有其他建议。
感谢您的帮助。
答案 0 :(得分:2)
这种情况是target_str用50个字节初始化。 source_str是长度为20的字符串。
如果我想将source_str复制到target_str,请使用上述长度为50的memcpy(),长度为target_str的大小。
当前,您要求 memcpy 在源字符串末尾读取30个字符,因为它不关心源上可能存在的空终止符,这是未定义的行为
因为您复制了字符串,所以可以使用 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
答案 1 :(得分:2)
如果我想将source_str复制到target_str,请使用上述的memcpy() 长度为50,即target_str的大小。我用的原因 memcpy中的长度是,source_str的最大值可以是 长度,但通常小于该长度(在上面的示例中为20)。
区别至关重要
source_str
指向的数组的大小,和source_str
指向的字符串的长度(如果有的话)(+/-终止符)。如果确定source_str
指向长度为50或更大的数组,那么您提出的memcpy()
方法是可以的。如果不是,那么当source_str
实际上指向较短的数组时,它将产生未定义的行为。在C实现的能力范围内可能会发生任何结果。
如果肯定source_str
指向不超过length - 1
个字符的(正确终止的)C字符串,并且要复制的是它的字符串值,则{{1 }}比strcpy()
更自然。它将复制所有字符串内容,直到终止符为止。只要memcpy()
指向比source_str
短的数组,只要它包含字符串终止符,就不会出现问题。
如果这两种情况都不能成立,则不清楚您想做什么。 length
函数可以涵盖其中一些情况,但不能涵盖所有情况。
答案 2 :(得分:1)
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
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)
的计算结果为假,它将破坏循环条件并提早退出。
因此,这会给您想要的行为。
答案 3 :(得分:1)
现在,如果我想根据其终止字符('\ 0')复制到source_str的长度,即使memcpy的长度大于终止字符的索引,上述代码是否也是正确的方法?
否;您将复制source_str
的全部内容,甚至复制到null终止符之后(如果该终止符出现在它指向的字符串的分配空间的末尾之前)。
如果您担心要最小化程序使用的辅助空间,则可以使用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;