memmove 和 memcpy 有什么区别?

发布于 2024-07-29 05:52:27 字数 75 浏览 6 评论 0 原文

memmovememcpy 之间有什么区别? 您通常使用哪一种以及如何使用?

What is the difference between memmove and memcpy? Which one do you usually use and how?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

鹿! 2024-08-05 05:52:27

使用memcpy,目标根本不能与源重叠。 使用 memmove 就可以了。 这意味着 memmove 可能比 memcpy 慢一点,因为它无法做出相同的假设。

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

With memcpy, the destination cannot overlap the source at all. With memmove it can. This means that memmove might be very slightly slower than memcpy, as it cannot make the same assumptions.

For example, memcpy might always copy addresses from low to high. If the destination overlaps after the source, this means some addresses will be overwritten before copied. memmove would detect this and copy in the other direction - from high to low - in this case. However, checking this and switching to another (possibly less efficient) algorithm takes time.

追星践月 2024-08-05 05:52:27

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

memmove can handle overlapping memory, memcpy can't.

Consider

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

Obviously the source and destination now overlap, we're overwriting
"-bar" with "bar". memcpy can cause undefined behavior if the source and destination overlap, so in this case we need memmove.

memmove(&str[3], &str[4], 4); // fine
深白境迁sunset 2024-08-05 05:52:27

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

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 因为它总是会产生正确的结果,并且通常,您需要的复印情况尽可能快。

  2. 如果您确定 srcdst 不重叠,请调用 memcpy,因为调用哪一个并不重要对于结果,在这种情况下两者都可以正常工作,但是 memmove 永远不会比 memcpy 更快,如果你不幸运,它甚至可能会更慢,所以你只能win 调用 memcpy

Assuming you would have to implement both, the implementation could look like that:

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

And this should pretty well explain the difference. memmove always copies in such a way, that it is still safe if src and dst overlap, whereas memcpy just doesn't care as the documentation says when using memcpy, the two memory areas must not overlap.

E.g. if memcpy copies "front to back" and the memory blocks are aligned as this

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

copying the first byte of src to dst already destroys the content of the last bytes of src before these have been copied. Only copying "back to front" will lead to correct results.

Now swap src and dst:

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

In that case it's only safe to copy "front to back" as copying "back to front" would destroy src near its front already when copying the first byte.

You may have noticed that the memmove implementation above doesn't even test if they actually do overlap, it just checks their relative positions, but that alone will make the copy safe. As memcpy usually uses the fastest way possible to copy memory on any system, memmove is usually rather implemented as:

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);
    }
}

Sometimes, if memcpy always copies "front to back" or "back to front", memmove may also use memcpy in one of the overlapping cases but memcpy may even copy in a different way depending on how the data is aligned and/or how much data is to be copied, so even if you tested how memcpy copies on your system, you cannot rely on that test result to be always correct.

What does that mean for you when deciding which one to call?

  1. Unless you know for sure that src and dst don't overlap, call memmove as it will always lead to correct results and is usually as fast as that is possible for the copy case you require.

  2. If you know for sure that src and dst don't overlap, call memcpy as it won't matter which one you call for the result, both will work correctly in that case, but memmove will never be faster than memcpy and if you are unlucky, it may even be slower, so you can only win calling memcpy.

快乐很简单 2024-08-05 05:52:27

来自 memcpy 手册页。

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

From the memcpy man page.

The memcpy() function copies n bytes
from memory area src to memory area
dest. The memory areas should not
overlap. Use memmove(3) if the memory
areas do overlap.

迷荒 2024-08-05 05:52:27

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

考虑以下示例:

  1. #include  
      #include ; 
    
      int main(空) 
      { 
          char string[] = "stackoverflow"; 
          字符*第一个,*第二个; 
          第一个=字符串; 
          第二个=字符串; 
    
          投入(字符串); 
          memcpy(第一个+5,第一个,5); 
          投入(第一); 
          memmove(第二+5, 第二, 5); 
          投入(第二); 
          返回0; 
      } 
      

    如您所料,这将打印出来:

    stackoverflow 
      堆栈堆栈低 
      堆栈堆栈低 
      
  2. 但在本示例中,结果将不同:

    #include ; 
      #include ; 
    
      int main(空) 
      { 
          char string[] = "stackoverflow"; 
          字符*第三,*第四; 
          第三=字符串; 
          第四个=字符串; 
    
          投入(字符串); 
          memcpy(第三+5, 第三, 7); 
          投入(第三); 
          memmove(第四个+5,第四个,7); 
          看跌期权(第四); 
          返回0; 
      } 
      

    输出:

    stackoverflow 
      堆栈stackovw 
      堆栈stackstw 
      

这是因为“memcpy()”执行以下操作:

1.  stackoverflow
2.  stacksverflow
3.  stacksterflow
4.  stackstarflow
5.  stackstacflow
6.  stackstacklow
7.  stackstacksow
8.  stackstackstw

The main difference between memmove() and memcpy() is that in memmove() a buffer - temporary memory - is used, so there is no risk of overlapping. On the other hand, memcpy() directly copies the data from the location that is pointed by the source to the location pointed by the destination. (http://www.cplusplus.com/reference/cstring/memcpy/)

Consider the following examples:

  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;
    }
    

    As you expected, this will print out:

    stackoverflow
    stackstacklow
    stackstacklow
    
  2. But in this example, the results will not be the same:

    #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;
    }
    

    Output:

    stackoverflow
    stackstackovw
    stackstackstw
    

It is because "memcpy()" does the following:

1.  stackoverflow
2.  stacksverflow
3.  stacksterflow
4.  stackstarflow
5.  stackstacflow
6.  stackstacklow
7.  stackstacksow
8.  stackstackstw
手长情犹 2024-08-05 05:52:27

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

One (memmove) handles overlapping destinations the other (memcpy) doesn't.

触ぅ动初心 2024-08-05 05:52:27

ISO/IEC:9899 标准对此进行了很好的描述。

7.21.2.1 memcpy 函数

[...]

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

并且

7.21.2.2 memmove 函数

[...]

2 memmove函数将s2指向的对象中的n个字符复制到
s1 指向的对象。 复制就像对象中的 n 个字符一样
s2 指向的内容首先被复制到一个包含 n 个字符的临时数组中,该数组不会
重叠s1和s2指向的对象,然后是n个字符
临时数组被复制到 s1 指向的对象中。


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

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

simply from the ISO/IEC:9899 standard it is well described.

7.21.2.1 The memcpy function

[...]

2 The memcpy function copies n characters from the object pointed to by s2 into the
object pointed to by s1. If copying takes place between objects that overlap, the behavior is undefined.

And

7.21.2.2 The memmove function

[...]

2 The memmove function copies n characters from the object pointed to by s2 into the
object pointed to by s1. Copying takes place as if the n characters from the object
pointed to by s2 are first copied into a temporary array of n characters that does not
overlap
the objects pointed to by s1 and s2, and then the n characters from the
temporary array are copied into the object pointed to by s1.

Which one I usually use acording to the question, depends on what functionallity I need.

In plain text memcpy() doesn't allow s1 and s2 to overlap, while memmove() does.

漫漫岁月 2024-08-05 05:52:27

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

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

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

最简单的 memmove() 实现将测试 dest (以某种依赖于平台的方式),并执行 memcpy() 的适当方向

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


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

There are two obvious ways to implement mempcpy(void *dest, const void *src, size_t n) (ignoring the return value):

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

In the first implementation, the copy proceeds from low to high addresses, and in the second, from high to low. If the range to be copied overlaps (as is the case when scrolling a framebuffer, for example), then only one direction of operation is correct, and the other will overwrite locations that will subsequently be read from.

A memmove() implementation, at its simplest, will test dest<src (in some platform-dependent way), and execute the appropriate direction of memcpy().

User code can't do that of course, because even after casting src and dst to some concrete pointer type, they don't (in general) point into the same object and so can't be compared. But the standard library can have enough platform knowledge to perform such a comparison without causing Undefined Behaviour.


Note that in real life, implementations tend to be significantly more complex, to gain the maximum performance from larger transfers (when alignment permits) and/or good data cache utilisation. The code above is just to make the point as simply as possible.

若沐 2024-08-05 05:52:27

在Windows操作系统中,ntdll.dll还导出名为memcpy和memmove的函数,它们实际上是同一个函数(以及RtlCopyMemory和RtlMoveMemory)功能)。 此屏幕截图来自 ntdll.dll 的 x64dbg 符号选项卡

在此处输入图像描述

In Windows operating system, ntdll.dll also exports functions named memcpy and memmove, and they are actually the same function (along with RtlCopyMemory and RtlMoveMemory functions). This screenshot is from x64dbg symbols tab for ntdll.dll

enter image description here

站稳脚跟 2024-08-05 05:52:27

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

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

memmove can deal with overlapping source and destination regions, while memcpy cannot. Among the two, memcpy is much more efficient. So, better to USE memcpy it if you can.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文