memmove和memcpy有什么区别?

问题描述 投票:106回答:9

memmovememcpy有什么区别?您经常使用哪一个以及如何使用?

c memcpy memmove
9个回答
149
投票

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

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


31
投票

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

22
投票

来自memcpy手册页。

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


13
投票

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

10
投票

一个处理重叠的目的地而另一个不处理。


6
投票

简单地从ISO / IEC:9899标准中对其进行了很好的描述。

7.21.2.1 memcpy函数

[...]

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

7.21.2.2 memmove功能

[...]

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

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

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


5
投票

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

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 mempy ( 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 ---]

src的第一个字节复制到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. 除非您确定srcdst不重叠,否则请调用memmove,因为它始终会产生正确的结果,并且通常与您所需的复制案例一样快。
  2. 如果你肯定知道srcdst没有重叠,请打电话给memcpy因为你要求哪一个结果无关紧要,在这种情况下两者都能正常工作,但memmove永远不会比memcpy快,如果你是不幸的是,它甚至可能更慢,所以你只能赢得呼叫memcpy

0
投票

实现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转换为某种具体的指针类型,它们也不会(通常)指向同一个对象,因此无法进行比较。但是标准库可以具有足够的平台知识来执行这样的比较而不会导致未定义的行为。


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


0
投票

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

参考:https://www.youtube.com/watch?v=Yr1YnOVG-4g Jerry Cain博士,(斯坦福大学简介系统讲座 - 7)时间:36:00

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