memmove和memcpy有什么区别?

nnvyjq4y  于 2023-04-11  发布在  其他
关注(0)|答案(9)|浏览(145)

memmovememcpy有什么区别?你通常使用哪一种?如何使用?

qacovj5a

qacovj5a1#

对于memcpy,目标根本不能与源重叠。对于memmove,它可以。这意味着memmove可能比memcpy稍微慢一点,因为它不能做出相同的假设。
例如,memcpy可能总是从低到高复制地址。如果目标地址在源地址之后重叠,这意味着一些地址将在复制之前被覆盖。在这种情况下,memmove会检测到这一点并在另一个方向复制-从高到低。但是,检查这一点并切换到另一个(可能效率较低)算法需要时间。

qybjjes1

qybjjes12#

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
kqqjbcuj

kqqjbcuj3#

假设你必须实现这两个,实现可能看起来像这样:

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

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,因为它将始终导致正确的结果,并且通常与您所需的复印情况一样快。
1.如果你确定srcdst不重叠,调用memcpy,因为你调用哪一个都没有关系,在这种情况下,两者都能正常工作,但是memmove永远不会比memcpy快,如果你不走运,它甚至可能更慢,所以你只能调用memcpy

lpwwtiir

lpwwtiir4#

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

u0sqgete

u0sqgete5#

memmove()memcpy()之间的主要区别是,在memmove()中使用了一个 buffer -临时内存,因此没有重叠的风险。另一方面,memcpy()直接将数据从 source 指向的位置复制到 destination 指向的位置。(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

1.但在这个例子中,结果将不一样:

#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
polhcujo

polhcujo6#

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

pbossiut

pbossiut7#

简单地从ISO/IEC:9899标准,它被很好地描述。
7.21.2.1 函数
[...]
2 memcpy函数从s2指向的对象复制n个字符到s1指向的对象。如果复制发生在重叠的对象之间,则行为未定义。
然后呢
7.21.2.2 函数
[...]
2 memmove函数将s2指向的对象中的n个字符复制到s1指向的对象中。复制的发生就好像s2指向的对象中的n个字符首先被复制到一个n个字符的临时数组中,该数组不与s1和s2指向的对象重叠,然后将来自临时数组的N个字符复制到由S1指向的对象中。
我通常根据问题使用哪一个,取决于我需要什么功能。
在纯文本中,memcpy()不允许s1s2重叠,而memmove()允许。

66bbxpm5

66bbxpm58#

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

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

在第一种实现中,复制从低地址到高地址进行,而在第二种实现中,从高地址到低地址进行。如果要复制的范围重叠(例如,当滚动帧缓冲器时就是这种情况),则只有一个操作方向是正确的,而另一个操作方向将覆盖随后将被读取的位置。
一个memmove()的实现,在其最简单的,将测试dest<src(在一些平台相关的方式),并执行适当的方向的memcpy()
用户代码当然不能这样做,因为即使将srcdst转换为某种具体的指针类型,它们(通常)也不会指向同一个对象,因此不能进行比较。但是标准库可以有足够的平台知识来执行这样的比较,而不会导致Undefined Behaviour。
请注意,在真实的生活中,实现往往要复杂得多,以便从更大的传输(当对齐允许时)和/或良好的数据缓存利用率中获得最大的性能。上面的代码只是为了尽可能简单地说明这一点。

r8uurelv

r8uurelv9#

memmove可以处理重叠的源区域和目标区域,而memcpy不能。在两者中,memcpy效率更高。所以,如果可以的话,最好使用memcpy。
参考文献:https://www.youtube.com/watch?v=Yr1YnOVG-4g Jerry Cain博士,(斯坦福大学大学系统介绍讲座-7)时间:三十六点

相关问题