memcpy() vs. memmove()

Lesezeit: 8 Minuten

Benutzeravatar von user534785
Benutzer534785

Ich versuche den Unterschied zwischen zu verstehen memcpy() und memmove()und ich habe den Text gelesen, dass memcpy() kümmert sich nicht um die Überlappung von Quelle und Ziel, während memmove() tut.

Wenn ich diese beiden Funktionen jedoch auf überlappenden Speicherblöcken ausführe, liefern sie beide das gleiche Ergebnis. Nehmen Sie zum Beispiel das folgende MSDN-Beispiel auf der memmove() Hilfeseite:-

Gibt es ein besseres Beispiel, um die Nachteile zu verstehen? memcpy und wie memmove löst es?

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

Ausgabe:

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

  • Die Microsoft CRT hat seit geraumer Zeit ein sicheres memcpy().

    – Hans Passant

    11. Dezember 2010 um 9:01 Uhr

  • Ich glaube nicht, dass “sicher” das richtige Wort dafür ist. Ein Tresor memcpy möchten assert dass sich die Regionen nicht überlappen, anstatt absichtlich Fehler in Ihrem Code zu verdecken.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    11. Dezember 2010 um 12:53 Uhr

  • Hängt davon ab, ob Sie “sicher für den Entwickler” oder “sicher für den Endbenutzer” meinen. Ich würde argumentieren, dass es für den Endbenutzer die sicherere Wahl ist, das Gesagte zu tun, auch wenn es nicht standardkonform ist.

    – Kusma

    26. Januar 2012 um 12:33 Uhr

  • Microsofts „sicheres“ memcpy() ist ein Fallback auf memmove() twitter.com/MalwareMinigun/status/737801492808142848

    – vobjekt

    1. Juni 2016 um 1:33 Uhr

  • Ein gutes Beispiel mit Bildern zum Thema „Was alles schief gehen kann memcpy(...) finden Sie hier: memcpy gegen memmove.

    – Deralbert

    4. Oktober 2020 um 14:18 Uhr

Benutzeravatar von developmentalinsanity
Entwicklungswahnsinn

Ich bin nicht ganz überrascht, dass Ihr Beispiel kein seltsames Verhalten zeigt. Versuchen Sie es mit Kopieren str1 zu str1+2 stattdessen und sehen, was dann passiert. (Möglicherweise keinen Unterschied machen, abhängig von Compiler/Bibliotheken.)

Im Allgemeinen ist memcpy auf einfache (aber schnelle) Weise implementiert. Vereinfacht gesagt werden die Daten einfach (in der Reihenfolge) durchlaufen und von einem Ort zum anderen kopiert. Dies kann dazu führen, dass die Quelle beim Lesen überschrieben wird.

Memmove leistet mehr Arbeit, um sicherzustellen, dass die Überlappung korrekt gehandhabt wird.

BEARBEITEN:

(Leider kann ich keine anständigen Beispiele finden, aber diese reichen aus). Kontrastieren Sie die memcpy und memmove hier gezeigten Implementierungen. memcpy macht einfach eine Schleife, während memmove einen Test durchführt, um zu bestimmen, in welche Richtung eine Schleife eingefügt werden soll, um eine Beschädigung der Daten zu vermeiden. Diese Implementierungen sind ziemlich einfach. Die meisten Hochleistungsimplementierungen sind komplizierter (sie beinhalten das gleichzeitige Kopieren von Blöcken in Wortgröße anstelle von Bytes).

  • +1 Auch in der folgenden Implementierung memmove Anrufe memcpy in einem Zweig nach dem Testen der Zeiger: student.cs.uwaterloo.ca/~cs350/common/os161-src-html/…

    – Pascal Cuoq

    11. Dezember 2010 um 9:15 Uhr

  • Das klingt gut. Anscheinend implementiert Visual Studio ein “sicheres” Memcpy (zusammen mit gcc 4.1.1 habe ich es auch auf RHEL 5 getestet). Das Schreiben der Versionen dieser Funktionen von clc-wiki.net ergibt ein klares Bild. Vielen Dank.

    – Benutzer534785

    11. Dezember 2010 um 9:24 Uhr

  • memcpy kümmert sich nicht um das Überlappungsproblem, aber memmove tut es. Warum dann memcpy nicht aus der Bibliothek entfernen?

    – Alcott

    16. September 2011 um 12:11 Uhr

  • @Alcott: Weil memcpy kann schneller sein.

    – Billy ONeal

    17. Oktober 2011 um 17:23 Uhr

  • Fester/Webarchiv-Link von Pascal Cuoq oben: web.archive.org/web/20130722203254/http://…

    – JWCS

    29. Mai 2020 um 16:43 Uhr

Benutzeravatar von rxantos
Rxantos

Die Erinnerung an memcpy kann nicht sich überschneiden oder Sie riskieren ein undefiniertes Verhalten, während die Erinnerung drin ist memmove überlappen können.

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. 

Einige Implementierungen von memcpy funktionieren möglicherweise immer noch für überlappende Eingaben, aber Sie können dieses Verhalten nicht zählen. Während memmove Überlappungen zulassen muss.

  • es hat mir wirklich geholfen, danke! +1 für deine Info

    – Muthu Ganapathy Nathan

    28. August 2011 um 12:41 Uhr

Nur weil memcpy nicht mit überlappenden Regionen umgehen muss, bedeutet nicht, dass es sie nicht korrekt behandelt. Der Aufruf mit überlappenden Regionen erzeugt ein undefiniertes Verhalten. Undefiniertes Verhalten kann auf einer Plattform vollständig so funktionieren, wie Sie es erwarten; das bedeutet nicht, dass es richtig oder gültig ist.

  • Insbesondere je nach Plattform ist das möglich memcpy wird genauso implementiert wie memmove. Das heißt, wer auch immer den Compiler geschrieben hat, hat sich nicht die Mühe gemacht, ein Unique zu schreiben memcpy Funktion.

    – Kamera

    11. Dezember 2010 um 8:46 Uhr

Benutzeravatar von Neilvert Noval
Neilvert Noval

Sowohl memcpy als auch memove machen ähnliche Dinge.

Aber um einen Unterschied auszumachen:

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

char str1[7] = "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("\nstr1: %s\n", str1);
   printf( "The string: %s\n", str1 );
   memmove( (str1+6), str1, 10 );
   printf( "New string: %s\n", str1 );

}

gibt:

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

Ihre Demo hat Memcpy-Nachteile wegen des “schlechten” Compilers nicht aufgedeckt, sie tut Ihnen in der Debug-Version einen Gefallen. Eine Release-Version gibt Ihnen jedoch die gleiche Ausgabe, jedoch aufgrund der Optimierung.

    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  

Das Register %eax spielt hier als temporärer Speicher, der das Überlappungsproblem “elegant” behebt.

Der Nachteil tritt auf, wenn 6 Bytes kopiert werden, zumindest ein Teil davon.

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

Ausgabe:

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

Sieht komisch aus, liegt auch an der Optimierung.

    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  

Deshalb wähle ich immer memmove beim Versuch, 2 überlappende Speicherblöcke zu kopieren.

Benutzeravatar von Pimgd
Pimgd

Der Unterschied zwischen memcpy und memmove ist das

  1. in memmove, wird der Quellspeicher der angegebenen Größe in den Puffer kopiert und dann zum Ziel verschoben. Wenn sich der Speicher also überlappt, gibt es keine Nebenwirkungen.

  2. im Falle von memcpy(), wird kein zusätzlicher Puffer für den Quellspeicher verwendet. Das Kopieren erfolgt direkt im Speicher, so dass wir bei einer Speicherüberlappung unerwartete Ergebnisse erhalten.

Diese können durch den folgenden Code beobachtet werden:

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

Ausgabe ist:

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

C11 Standardentwurf

Das C11 N1570 Standardentwurf sagt:

7.24.2.1 „Die memcpy-Funktion“:

2 Die Funktion memcpy kopiert n Zeichen aus dem Objekt, auf das s2 zeigt, in das Objekt, auf das s1 zeigt. Wenn zwischen sich überlappenden Objekten kopiert wird, ist das Verhalten undefiniert.

7.24.2.2 „Die memmove-Funktion“:

2 Die Funktion memmove kopiert n Zeichen aus dem Objekt, auf das s2 zeigt, in das Objekt, auf das s1 zeigt. Das Kopieren erfolgt so, als ob die n Zeichen aus dem Objekt, auf das s2 zeigt, zuerst in ein temporäres Array aus n Zeichen kopiert werden, das die Objekte, auf die s1 und s2 zeigen, nicht überlappt, und dann werden die n Zeichen aus dem temporären Array hineinkopiert das Objekt, auf das s1 zeigt

Daher überlappen sich keine memcpy führt zu undefiniertem Verhalten, und alles kann passieren: schlecht, nichts oder sogar gut. Gut ist selten 🙂

memmove sagt aber eindeutig, dass alles so passiert, als ob ein Zwischenpuffer verwendet wird, also sind eindeutige Überschneidungen OK.

C++ std::copy ist jedoch nachsichtiger und erlaubt Überlappungen: Behandelt std::copy überlappende Bereiche?

  • memmove Verwenden Sie ein zusätzliches temporäres Array von n, also wird zusätzlicher Speicher verwendet? Aber wie kann es, wenn wir ihm keinen Zugriff auf irgendeinen Speicher gegeben haben. (Es verwendet 2x den Speicher).

    – Clementjohn

    27. März 2019 um 4:42 Uhr


  • @clmno ordnet es wie jede andere Funktion, die ich erwarten würde, auf Stack oder Malloc zu 🙂

    – Ciro Santilli OurBigBook.com

    27. März 2019 um 18:30 Uhr

  • Ich hatte hier eine Frage gestellt, bekam auch eine gute Antwort. Vielen Dank. Ich habe Ihre Hackernews gesehen Post das ging viral (das x86) 🙂

    – Clementjohn

    28. März 2019 um 4:11 Uhr

1425530cookie-checkmemcpy() vs. memmove()

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy