C - 带有char *的memcpy,长度大于源字符串长度

问题描述 投票:2回答:4

我现在在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长度超过终止字符的索引,上面的代码是正确的方法吗?还是有其他建议。

谢谢你的帮助。

c string memcpy strcpy
4个回答
2
投票

如果我想将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()函数可能涵盖其中一些案例,但并不涵盖所有这些案例。


2
投票

场景是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

1
投票

现在,如果我想基于其终止字符('\ 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;

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的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并将打破循环条件并提前退出。

因此,这给你想要的行为。

© www.soinside.com 2019 - 2024. All rights reserved.