memmove와 memcpy의 차이점은 무엇입니까?


답변:


162

를 사용 memcpy하면 대상이 소스와 전혀 겹칠 수 없습니다. 로 memmove가 있습니다. 이는 다음 memmove보다 약간 느릴 수 있음을 의미합니다.memcpy 동일한 가정을 할 수 없기 때문에 .

예를 들어 memcpy는 항상 낮은 주소에서 높은 주소로 주소를 복사 할 수 있습니다. 대상이 소스 뒤에 겹치는 경우 복사하기 전에 일부 주소를 덮어 쓰게됩니다. memmove이 경우이를 감지하고 다른 방향 (높은 방향에서 낮은 방향으로)으로 복사합니다. 그러나 이것을 확인하고 다른 알고리즘으로 전환하는 데 시간이 걸립니다.


1
memcpy를 사용할 때 src 및 dest 주소가 겹치지 않도록 어떻게 보장 할 수 있습니까? src와 dest가 겹치지 않도록 개인적으로 확인해야합니까?
Alcott

6
@Alcott, 겹치지 않는다는 것을 모르는 경우 memcpy를 사용하지 마십시오. 대신 memmove를 사용하십시오. 겹침이 없으면 memmove와 memcpy는 동일합니다 (memcpy가 아주, 아주, 아주 조금 더 빠를 수 있지만).
bdonlan 2011 년

긴 배열로 작업하고 복사 프로세스를 보호하려는 경우 'restrict'키워드를 사용할 수 있습니다. 예를 들어 메소드가 매개 변수 입력 및 출력 배열을 취하고 사용자가 입력 및 출력과 동일한 주소를 전달하지 않는지 확인해야하는 경우. 자세한 내용은 여기를 참조하십시오. stackoverflow.com/questions/776283/…
DanielHsH

10
@DanielHsH 'restrict'는 컴파일러를 약속하는 것입니다. 컴파일러에 의해 강제 되지 않습니다 . 인수에 '제한'을 적용하고 실제로 겹치는 경우 (또는보다 일반적으로 여러 위치에서 파생 된 포인터에서 제한된 데이터에 액세스) 프로그램의 동작이 정의되지 않고 이상한 버그가 발생하고 컴파일러가 발생합니다. 일반적으로 그것에 대해 경고하지 않습니다.
bdonlan 2015

@bdonlan 컴파일러에 대한 약속이 아니라 호출자를위한 요구 사항입니다. 시행되지 않는 요구 사항이지만 요구 사항을 위반하면 예상치 못한 결과가 발생해도 불만을 제기 할 수 없습니다. 요구 사항 위반은 정의되지 않은 것처럼 정의되지 않은 동작 i = i++ + 1입니다. 컴파일러는 해당 코드를 정확하게 작성하는 것을 금지하지 않지만 해당 명령의 결과는 무엇이든 될 수 있으며 다른 컴파일러 또는 CPU는 여기에서 다른 값을 표시합니다.
Mecki

33

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

5
왜 먼저 폭파할까요?

4
@ultraman : 메모리가 겹치지 않아야하는 저수준 어셈블리를 사용하여 구현되었을 수 있기 때문입니다. 예를 들어 응용 프로그램을 중단하는 프로세서에 대한 신호 또는 하드웨어 예외를 생성 할 수 있습니다. 문서에는 조건을 처리하지 않는다고 명시되어 있지만 표준은 이러한 조건을 통해 발생하는 일을 지정하지 않습니다 (이를 정의되지 않은 동작이라고 함). 정의되지 않은 동작은 무엇이든 할 수 있습니다.
Martin York

gcc 4.8.2를 사용하면 memcpy조차도 겹치는 소스 및 대상 포인터를 허용하고 제대로 작동합니다.
GeekyJ

4
@jagsgediya 물론입니다. 그러나 memcpy는이를 지원하지 않는 것으로 문서화되었으므로 해당 구현 특정 동작에 의존해서는 안됩니다. 이것이 memmove ()가 존재하는 이유입니다. 다른 버전의 gcc에서는 다를 수 있습니다. gcc가 glibc에서 memcpy ()를 호출하는 대신 memcpy를 인라인하는 경우 다를 수 있으며, glibc의 이전 버전이나 최신 버전 등에서 다를 수 있습니다.
nos

연습에서 memcpy와 memmove가 같은 일을 한 것 같습니다. 그렇게 깊이 정의되지 않은 행동.
Life

22

로부터 방어 적이기 매뉴얼 페이지를 참조하십시오.

memcpy () 함수는 src 메모리 영역에서 dest 메모리 영역으로 n 바이트를 복사합니다. 메모리 영역은 겹치지 않아야합니다. 메모리 영역이 겹치는 경우 memmove (3)를 사용하십시오 .


12

주요 차이점 memmove()memcpy()에 있다는 것이다 버퍼 임시 메모리 - - 사용되므로 중첩의 위험이 없다. 반면에 의해 지시되는 위치에서 직접적으로 데이터를 복사하는 소스 위치로가 가리키는 도착 . ( http://www.cplusplus.com/reference/cstring/memcpy/memmove()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

2
그러나 언급 한 출력이 반전 된 것 같습니다 !!
kumar 2014

1
동일한 프로그램을 실행하면 다음과 같은 결과가 나타납니다. stackoverflow stackstackstw stackstackstw // memcpy와 memmove간에 출력에 차이가 없음을 의미합니다.
kumar

4
""memmove () "에서 임시 메모리 인 버퍼가 사용된다는 것입니다." 사실이 아닙니다. "마치"라고되어 있으므로 그렇게해야하는 것이 아니라 그렇게 행동해야합니다. 대부분의 memmove 구현은 XOR-swap을 수행하므로 실제로 관련이 있습니다.
dhein

2
memmove()버퍼를 사용하기 위해 구현 이 필요 하다고 생각하지 않습니다 . (동일한 주소에 쓰기 전에 각 읽기가 완료되는 한) 제자리로 이동할 수 있습니다.
Toby Speight

12

둘 다 구현해야한다고 가정하면 구현은 다음과 같을 수 있습니다.

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

의 첫 번째 바이트 복사 srcdst의 마지막 바이트의 내용은 이미 파괴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. 확실하게 알고 src있고 dst겹치지 않는 한, memmove항상 정확한 결과를 얻을 수 있고 일반적으로 필요한 복사 사례에 대해 가능한 한 빠르기 때문에 전화하십시오.

  2. 확실히 알고 src있고 dst겹치지 memcpy않으면 결과를 위해 어떤 것을 호출하든 상관 없으므로 전화하십시오.이 경우 둘 다 올바르게 작동하지만 memmove결코 더 빠르지 않으며 memcpy운이 좋지 않으면 천천히, 그래서 당신은 전화를 이길 수 있습니다 memcpy.


하나는 "아스키 그림은"데이터를 손상없이이 중복되지 않는 이유를 이해하는 데 도움이 있었기 때문에
Scylardor

10

하나는 중복 대상을 처리하고 다른 하나는 처리하지 않습니다.


6

ISO / IEC : 9899 표준에서 간단하게 설명되어 있습니다.

7.21.2.1 memcpy 함수

[...]

2 memcpy 함수는 s2가 가리키는 객체에서 s1이 가리키는 객체로 n 개의 문자를 복사합니다. 겹치는 객체간에 복사가 발생하면 동작이 정의되지 않습니다.

7.21.2.2 memmove 기능

[...]

2 memmove 함수는 s2가 가리키는 개체에서 s1이 가리키는 개체로 n 개의 문자를 복사합니다. 복사는 s2가 가리키는 객체의 n 문자가 먼저 s1 및 s2가 가리키는 객체와 겹치지 않는 n 문자의 임시 배열에 복사 된 다음 임시 배열의 n 문자가 복사되는 것처럼 발생합니다. s1이 가리키는 객체.

질문에 따라 일반적으로 사용하는 것은 필요한 기능에 따라 다릅니다.

일반 텍스트에서 memcpy()허용하지 않는 s1s2중복에, 잠시는 memmove()않습니다.


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 Dr. Jerry Cain, (Stanford Intro Systems Lecture-7) 시간 : 36:00


이 답변 은 "아마도 더 빨리"라고 말하고 약간의 차이 만 나타내는 정량적 데이터를 제공합니다. 이 대답은 하나가 "훨씬 더 효율적"이라고 주장합니다. 더 빠른 것을 얼마나 더 효율적으로 찾았습니까? BTW : 나는 당신이 의미하는 memcpy()것이 아니라 memcopy().
chux - 분석 재개 모니카

코멘트는 Dr. Jerry Cain의 강의를 바탕으로 작성되었습니다. 36:00에 그의 강의를 들어달라고 요청합니다. 2-3 분이면 충분합니다. 그리고 캐치 주셔서 감사합니다. : D
에산
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.