C-具有char *且长度大于源字符串长度的memcpy

时间:2019-04-24 21:02:36

标签: c string memcpy strcpy

我现在在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大于终止字符的索引,上述代码是否正确?办法吗?还是有其他建议。

感谢您的帮助。

4 个答案:

答案 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。另外,strcpymemcpy类似,但专门用于以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;