132

memmove和 和有什么不一样memcpy?您通常使用哪一种以及如何使用?

4

9 回答 9

171

使用memcpy,目的地根本不能与源重叠。有了memmove它就可以。这意味着memmove可能会比 慢很多memcpy,因为它不能做出相同的假设。

例如,memcpy可能总是将地址从低位复制到高位。如果目标在源之后重叠,这意味着某些地址将在复制之前被覆盖。memmove在这种情况下,将检测到这一点并从另一个方向复制 - 从高到低。但是,检查这一点并切换到另一个(可能效率较低的)算法需要时间。

于 2009-07-29T16:03:17.163 回答
34

memmove可以处理重叠的内存,memcpy不能。

考虑

char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up

显然源和目标现在重叠,我们用“bar”覆盖“-bar”。如果源和目标重叠,这是未定义的行为memcpy,因此在这种情况下,我们需要memmove.

memmove(&str[3],&str[4],4); //fine
于 2009-07-29T16:03:53.153 回答
23

memcpy手册页。

memcpy() 函数将 n 个字节从内存区域 src 复制到内存区域 dest。内存区域不应重叠。如果内存区域确实重叠,请使用memmove (3)。

于 2009-07-29T16:02:41.830 回答
21

假设您必须同时实现两者,则实现可能如下所示:

void memmove ( void * dst, const void * src, size_t count ) {
    if ((uintptr_t)src < (uintptr_t)dst) {
        // Copy from back to front

    } else if ((uintptr_t)dst < (uintptr_t)src) {
        // Copy from front to back
    }
}

void memcpy ( void * dst, const void * src, size_t count ) {
    if ((uintptr_t)src != (uintptr_t)dst) {
        // Copy in any way you want
    }
}

这应该很好地解释了差异。memmove总是以这样的方式复制,如果srcdst重叠它仍然是安全的,而memcpy正如文档所说的那样,使用时并不关心memcpy,两个内存区域不能重叠。

例如,如果memcpy复制“从前到后”并且内存块是这样对齐的

[---- src ----]
            [---- dst ---]

复制srcto的第一个字节dst已经破坏了src复制之前最后一个字节的内容。只有“从后到前”复制才能得到正确的结果。

现在交换srcdst

[---- dst ----]
            [---- src ---]

在这种情况下,复制“从前到后”是安全的,因为src在复制第一个字节时,复制“从后到前”会破坏它的前面。

您可能已经注意到,memmove上面的实现甚至没有测试它们是否真的重叠,它只是检查它们的相对位置,但仅此一点就可以使副本安全。由于memcpy通常使用最快的方式在任何系统上复制内存,memmove通常是这样实现的:

void memmove ( void * dst, const void * src, size_t count ) {
    if ((uintptr_t)src < (uintptr_t)dst
        && (uintptr_t)src + count > (uintptr_t)dst
    ) {
        // Copy from back to front

    } else if ((uintptr_t)dst < (uintptr_t)src
        && (uintptr_t)dst + count > (uintptr_t)src
    ) {
        // Copy from front to back

    } else {
        // They don't overlap for sure
        memcpy(dst, src, count);
    }
}

有时,如果memcpy总是“从前到后”或“从后到前”复制,memmove也可以memcpy在其中一种重叠情况下使用,但memcpy甚至可以根据数据的对齐方式和/或数据量以不同的方式复制复制,因此即使您测试了memcpy系统上的复制方式,您也不能依赖该测试结果始终正确。

在决定打电话给哪一个时,这对您意味着什么?

  1. 除非您确定src并且dst不重叠,否则调用memmove,因为它总是会导致正确的结果,并且对于您需要的复制案例通常尽可能快。

  2. 如果您确定src并且dst不重叠,请调用memcpy,因为无论您调用哪个结果都无关紧要,在这种情况下两者都可以正常工作,但memmove永远不会比你更快memcpy,如果你不走运,它甚至可能慢一点,所以你只能赢得 call memcpy

于 2018-04-14T23:59:50.227 回答
12

memmove()和之间的主要区别在于memcpy(),在缓冲区memmove()中使用了临时内存,因此没有重叠的风险。另一方面,直接将数据从指向的位置复制到目标指向的位置。( http://www.cplusplus.com/reference/cstring/memcpy/ )memcpy()

考虑以下示例:

  1. #include <stdio.h>
    #include <string.h>
    
    int main (void)
    {
        char string [] = "stackoverflow";
        char *first, *second;
        first = string;
        second = string;
    
        puts(string);
        memcpy(first+5, first, 5);
        puts(first);
        memmove(second+5, second, 5);
        puts(second);
        return 0;
    }
    

    如您所料,这将打印出:

    stackoverflow
    stackstacklow
    stackstacklow
    
  2. 但在这个例子中,结果会不一样:

    #include <stdio.h>
    #include <string.h>
    
    int main (void)
    {
        char string [] = "stackoverflow";
        char *third, *fourth;
        third = string;
        fourth = string;
    
        puts(string);
        memcpy(third+5, third, 7);
        puts(third);
        memmove(fourth+5, fourth, 7);
        puts(fourth);
        return 0;
    }
    

    输出:

    stackoverflow
    stackstackovw
    stackstackstw
    

这是因为“memcpy()”做了以下事情:

1.  stackoverflow
2.  stacksverflow
3.  stacksterflow
4.  stackstarflow
5.  stackstacflow
6.  stackstacklow
7.  stackstacksow
8.  stackstackstw
于 2014-08-07T10:11:52.140 回答
10

一个 ( memmove) 处理重叠的目的地,另一个 ( memcpy) 不处理。

于 2009-07-29T16:02:37.150 回答
7

简单地从 ISO/IEC:9899 标准中得到了很好的描述。

7.21.2.1 memcpy 函数

[...]

2 memcpy函数将n个字符从s2指向的对象复制到s1指向的对象中。如果复制发生在重叠的对象之间,则行为未定义。

7.21.2.2 memmove 函数

[...]

2 memmove函数将n个字符从s2指向的对象复制到s1指向的对象中。复制的过程就像先将 s2 指向的对象中的 n 个字符复制到不与s1 和 s2 指向的对象重叠的 n 个字符的临时数组中,然后将临时数组中的 n 个字符复制到s1 指向的对象。

根据问题,我通常使用哪一个,取决于我需要什么功能。

在纯文本memcpy()中不允许s1s2重叠,而允许memmove()

于 2015-05-08T10:48:32.097 回答
1

有两种明显的实现方式mempcpy(void *dest, const void *src, size_t n)(忽略返回值):

  1. for (char *p=src, *q=dest;  n-->0;  ++p, ++q)
        *q=*p;
    
  2. char *p=src, *q=dest;
    while (n-->0)
        q[n]=p[n];
    

在第一种实现中,复制从低地址到高地址,在第二种实现中,从高地址到低地址。如果要复制的范围重叠(例如滚动帧缓冲区时的情况),则只有一个操作方向是正确的,而另一个将覆盖随后将读取的位置。

最简单的memmove()实现将测试dest<src(以某种平台相关的方式),并执行memcpy().

用户代码当然不能这样做,因为即使在强制转换srcdst一些具体的指针类型之后,它们(通常)也不会指向同一个对象,因此无法进行比较。但是标准库可以有足够的平台知识来执行这样的比较而不会导致未定义的行为。


请注意,在现实生活中,实现往往要复杂得多,以便从更大的传输(当对齐允许时)和/或良好的数据缓存利用率中获得最大性能。上面的代码只是为了尽可能简单地说明这一点。

于 2016-05-30T15:18:05.470 回答
0

memmove 可以处理重叠的源和目标区域,而 memcpy 不能。在这两者中,memcpy 的效率要高得多。所以,如果可以的话,最好使用 memcpy。

参考:https ://www.youtube.com/watch?v= Yr1YnOVG-4g Dr. Jerry Cain,(Stanford Intro Systems Lecture - 7)时间:36:00

于 2016-12-10T02:46:01.740 回答