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

有帮助吗?

解决方案

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

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

其他提示

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

考虑

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

显然,源和目的地现在重叠,我们正在覆盖 QUOT <!>; <!> -bar QUOT;用<!>“bar <!>”;如果源是<=>,则是未定义的行为 和目的地重叠所以在这种情况下我们需要<=>。

memmove(&str[3],&str[4],4); //fine

来自 memcpy 手册页。

  

memcpy()函数复制n个字节   从内存区域src到内存区域   DEST。内存区域不应该   交叠。使用 memmove (3)如果记忆   区域重叠。

之间的主要区别 memmove()memcpy() 是在 memmove() A 缓冲 - 使用临时内存,因此不存在重叠的风险。另一方面, 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

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

简单地从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()则重叠。

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

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只是不关心文档在使用<=>时所说的那么两个内存区域一定不能重叠。

E.g。 if <=> copy <!> quot; front to back <!> quot;并且内存块按此对齐

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

将<=>的第一个字节复制到<=>已经破坏了<=>的最后一个字节的内容,然后再复制这些字节。仅复制<!>“;返回前面<!>”;将导致正确的结果。

现在交换<=>和<=>:

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

在这种情况下,只能安全地复制<!>“;从前到后<!>”;复制<!>“;回到前面<!>”;在复制第一个字节时,它会在前面附近销毁<=>。

你可能已经注意到上面的<=>实现甚至没有测试它们是否确实重叠,它只是检查它们的相对位置,但仅此一项将使副本安全。由于<=>通常使用最快的方式在任何系统上复制内存,<=>通常实现为:

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

有时,如果<=>始终复制<!>“;从前到后<!>或<!>“;回到前面<!>”,<=>也可以在其中一个重叠的情况下使用<=>,但<=>甚至可能以不同的方式复制,具体取决于数据的对齐方式和/或者要复制多少数据,因此即使您在系统上测试了<=>副本的方式,也不能依赖该测试结果始终是正确的。

在决定调用哪一个时,这对您意味着什么?

  1. 除非您确定<=>和<=>不重叠,否则请致电<=>,因为它始终会产生正确的结果,并且通常与复制案例的速度一样快需要。

  2. 如果您确定<=>和<=>不重叠,请致电<=>,因为您调用哪一个结果无关紧要,在这种情况下两者都能正常工作,但是<=>永远不会比<=>更快,如果你运气不好,它甚至可能会变慢,所以你只能赢得<=>。

有两种明显的实现方法 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];
    

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

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

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


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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top