IT

memcpy () vs memmove ()

lottoking 2020. 6. 15. 08:06
반응형

memcpy () vs memmove ()


나는 차이를 이해하려고 memcpy()하고 memmove(), 나는 텍스트를 읽을 수있는 memcpy()반면, 중복 소스의 관심과 대상을 고려하지 않습니다 memmove()않습니다를.

그러나 겹치는 메모리 블록 에서이 두 기능을 실행하면 둘 다 동일한 결과를 얻습니다. 예를 들어, memmove()도움말 페이지 에서 다음 MSDN 예제를 보자 .

의 단점을 이해하는 좋은 예 거기에 memcpy어떻게 memmove해결할 수있는 문제는?

// crt_memcpy.c
// Illustrate overlapping copy: memmove always handles it correctly; memcpy may handle
// it correctly.

#include <memory.h>
#include <string.h>
#include <stdio.h>

char str1[7] = "aabbcc";

int main( void )
{
    printf( "The string: %s\n", str1 );
    memcpy( str1 + 2, str1, 4 );
    printf( "New string: %s\n", str1 );

    strcpy_s( str1, sizeof(str1), "aabbcc" );   // reset string

    printf( "The string: %s\n", str1 );
    memmove( str1 + 2, str1, 4 );
    printf( "New string: %s\n", str1 );
}

산출:

The string: aabbcc
New string: aaaabb
The string: aabbcc
New string: aaaabb

나는 당신의 모범이 이상한 행동을 보이지 않는다는 것에 완전히 놀랐습니다. 복사 시도 str1str1+2대신하고 어떻게되는지. (실제로 차이를 만들 수는 없으며 컴파일러 / 라이브러리에 따라 다릅니다.)

일반적으로 memcpy는 단순하지만 빠른 방식으로 구현됩니다. 간단히 말해서 데이터를 한 위치에서 다른 위치로 복사하여 순서대로 반복합니다. 이로 인해 소스를 읽는 동안 소스를 덮어 쓸 수 있습니다.

Memmove는 오버랩을 올바르게 처리하기 위해 더 많은 작업을 수행합니다.

편집하다:

(불행히도 괜찮은 예를 찾을 수는 없지만 그렇게 할 것입니다). 여기에 표시된 memcpymemmove 구현을 대조 하십시오. memcpy는 루프 만하는 반면 memmove는 데이터 손상을 피하기 위해 루프 할 방향을 결정하는 테스트를 수행합니다. 이러한 구현은 다소 간단합니다. 대부분의 고성능 구현은 바이트가 아닌 한 번에 워드 크기 블록을 복사하는 것과 관련하여 더 복잡합니다.


메모리가 겹칠 memcpy 수 없거나 정의되지 않은 동작의 위험이있는 반면 메모리 memmove가 겹칠 수 있습니다.

char a[16];
char b[16];

memcpy(a,b,16);           // valid
memmove(a,b,16);          // Also valid, but slower than memcpy.
memcpy(&a[0], &a[1],10);  // Not valid since it overlaps.
memmove(&a[0], &a[1],10); // valid. 

memcpy의 일부 구현은 여전히 ​​겹치는 입력에 대해 작동 할 수 있지만 해당 동작을 계산할 수는 없습니다. memmove가 겹치는 것을 허용해야합니다.


그냥 있기 때문에 memcpy중복 영역을 처리하지 않고, 올바르게 그들과 거래를하지 않는 것을 의미하지 않는다. 영역이 겹치는 호출은 정의되지 않은 동작을 생성합니다. 정의되지 않은 동작은 하나의 플랫폼에서 예상 한대로 작동 할 수 있습니다. 그것이 정확하거나 유효하다는 것을 의미하지는 않습니다.


memcpy와 memove는 비슷한 작업을 수행합니다.

그러나 한 가지 차이점을 알 수 있습니다.

#include <memory.h>
#include <string.h>
#include <stdio.h>

char str1[17] = "abcdef";

int main()
{

   printf( "The string: %s\n", str1 );
   memcpy( (str1+6), str1, 10 );
   printf( "New string: %s\n", str1 );

   strcpy_s( str1, sizeof(str1), "aabbcc" );   // reset string

   printf( "The string: %s\n", str1 );
   memmove( (str1+6), str1, 10 );
   printf( "New string: %s\n", str1 );

}

제공합니다 :

The string: abcdef
New string: abcdefabcdefabcd
The string: abcdef
New string: abcdefabcdef

데모는 "나쁜"컴파일러로 인해 memcpy 단점을 노출하지 않았으므로 디버그 버전에서 유리합니다. 그러나 릴리스 버전은 동일한 출력을 제공하지만 최적화 때문입니다.

    memcpy(str1 + 2, str1, 4);
00241013  mov         eax,dword ptr [str1 (243018h)]  // load 4 bytes from source string
    printf("New string: %s\n", str1);
00241018  push        offset str1 (243018h) 
0024101D  push        offset string "New string: %s\n" (242104h) 
00241022  mov         dword ptr [str1+2 (24301Ah)],eax  // put 4 bytes to destination
00241027  call        esi  

여기서 레지스터 %eax는 임시 저장소로 재생되며 겹치는 문제를 "우아하게"수정합니다.

6 바이트를 복사 할 때 적어도 일부는 복사 할 때 단점이 발생합니다.

char str1[9] = "aabbccdd";

int main( void )
{
    printf("The string: %s\n", str1);
    memcpy(str1 + 2, str1, 6);
    printf("New string: %s\n", str1);

    strcpy_s(str1, sizeof(str1), "aabbccdd");   // reset string

    printf("The string: %s\n", str1);
    memmove(str1 + 2, str1, 6);
    printf("New string: %s\n", str1);
}

산출:

The string: aabbccdd
New string: aaaabbbb
The string: aabbccdd
New string: aaaabbcc

이상하게 보입니다. 최적화 때문이기도합니다.

    memcpy(str1 + 2, str1, 6);
00341013  mov         eax,dword ptr [str1 (343018h)] 
00341018  mov         dword ptr [str1+2 (34301Ah)],eax // put 4 bytes to destination, earlier than the above example
0034101D  mov         cx,word ptr [str1+4 (34301Ch)]  // HA, new register! Holding a word, which is exactly the left 2 bytes (after 4 bytes loaded to %eax)
    printf("New string: %s\n", str1);
00341024  push        offset str1 (343018h) 
00341029  push        offset string "New string: %s\n" (342104h) 
0034102E  mov         word ptr [str1+6 (34301Eh)],cx  // Again, pulling the stored word back from the new register
00341035  call        esi  

이것이 memmove2 개의 겹친 메모리 블록을 복사하려고 할 때 항상 선택하는 이유 입니다.


차이 memcpy하고 memmove있다는 것이다

  1. 에서 memmove지정된 크기의 소스 메모리가 버퍼에 복사 된 다음 대상으로 이동됩니다. 따라서 메모리가 겹치는 경우 부작용이 없습니다.

  2. in case of memcpy(), there is no extra buffer taken for source memory. The copying is done directly on the memory so that when there is memory overlap, we get unexpected results.

These can be observed by the following code:

//include string.h, stdio.h, stdlib.h
int main(){
  char a[]="hare rama hare rama";

  char b[]="hare rama hare rama";

  memmove(a+5,a,20);
  puts(a);

  memcpy(b+5,b,20);
  puts(b);
}

Output is:

hare hare rama hare rama
hare hare hare hare hare hare rama hare rama

As already pointed out in other answers, memmove is more sophisticated than memcpy such that it accounts for memory overlaps. The result of memmove is defined as if the src was copied into a buffer and then buffer copied into dst. This does NOT mean that the actual implementation uses any buffer, but probably does some pointer arithmetic.


compiler could optimize memcpy, for example:

int x;
memcpy(&x, some_pointer, sizeof(int));

This memcpy may be optimized as: x = *(int*)some_pointer;


The code given in the links http://clc-wiki.net/wiki/memcpy for memcpy seems to confuse me a bit, as it does not give the same output when I implemented it using the below example.

#include <memory.h>
#include <string.h>
#include <stdio.h>

char str1[11] = "abcdefghij";

void *memcpyCustom(void *dest, const void *src, size_t n)
{
    char *dp = (char *)dest;
    const char *sp = (char *)src;
    while (n--)
        *dp++ = *sp++;
    return dest;
}

void *memmoveCustom(void *dest, const void *src, size_t n)
{
    unsigned char *pd = (unsigned char *)dest;
    const unsigned char *ps = (unsigned char *)src;
    if ( ps < pd )
        for (pd += n, ps += n; n--;)
            *--pd = *--ps;
    else
        while(n--)
            *pd++ = *ps++;
    return dest;
}

int main( void )
{
    printf( "The string: %s\n", str1 );
    memcpy( str1 + 1, str1, 9 );
    printf( "Actual memcpy output: %s\n", str1 );

    strcpy_s( str1, sizeof(str1), "abcdefghij" );   // reset string

    memcpyCustom( str1 + 1, str1, 9 );
    printf( "Implemented memcpy output: %s\n", str1 );

    strcpy_s( str1, sizeof(str1), "abcdefghij" );   // reset string

    memmoveCustom( str1 + 1, str1, 9 );
    printf( "Implemented memmove output: %s\n", str1 );
    getchar();
}

Output :

The string: abcdefghij
Actual memcpy output: aabcdefghi
Implemented memcpy output: aaaaaaaaaa
Implemented memmove output: aabcdefghi

But you can now understand why memmove will take care of overlapping issue.


C11 standard draft

The C11 N1570 standard draft says:

7.24.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.

7.24.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

Therefore, any overlap on memcpy leads to undefined behavior, and anything can happen: bad, nothing or even good. Good is rare though :-)

memmove however clearly says that everything happens as if an intermediate buffer is used, so clearly overlaps are OK.

C++ std::copy is more forgiving however, and allows overlaps: Does std::copy handle overlapping ranges?


I have tried to run same program using eclipse and it shows clear difference between memcpy and memmove. memcpy() doesn't care about overlapping of memory location which results in corruption of data, while memmove() will copy data to temporary variable first and then copy into actual memory location.

While trying to copy data from location str1 to str1+2, output of memcpy is "aaaaaa". The question would be how? memcpy() will copy one byte at a time from left to right. As shown in your program "aabbcc" then all copying will take place as below,

  1. aabbcc -> aaabcc

  2. aaabcc -> aaaacc

  3. aaaacc -> aaaaac

  4. aaaaac -> aaaaaa

memmove() will copy data to temporary variable first and then copy to actual memory location.

  1. aabbcc(actual) -> aabbcc(temp)

  2. aabbcc(temp) -> aaabcc(act)

  3. aabbcc(temp) -> aaaacc(act)

  4. aabbcc(temp) -> aaaabc(act)

  5. aabbcc(temp) -> aaaabb(act)

Output is

memcpy : aaaaaa

memmove : aaaabb

참고URL : https://stackoverflow.com/questions/4415910/memcpy-vs-memmove

반응형