Was ist der Unterschied zwischen memmove und memcpy?

Lesezeit: 9 Minuten

Was ist der Unterschied zwischen memmove und memcpy? Welches verwendest du normalerweise und wie?

Benutzeravatar von bdonlan
bdonlan

Mit memcpy, darf das Ziel die Quelle überhaupt nicht überlappen. Mit memmove es kann. Das bedeutet, dass memmove könnte etwas langsamer sein als memcpyda sie nicht dieselben Annahmen treffen kann.

Zum Beispiel, memcpy möglicherweise immer Adressen von niedrig nach hoch kopieren. Wenn sich das Ziel nach der Quelle überschneidet, bedeutet dies, dass einige Adressen vor dem Kopieren überschrieben werden. memmove würde dies in diesem Fall erkennen und in die andere Richtung – von hoch nach niedrig – kopieren. Dies zu überprüfen und zu einem anderen (möglicherweise weniger effizienten) Algorithmus zu wechseln, erfordert jedoch Zeit.

  • Wie kann ich bei der Verwendung von memcpy sicherstellen, dass sich die src- und dest-Adressen nicht überschneiden? Sollte ich persönlich darauf achten, dass sich src und dest nicht überschneiden?

    – Alcott

    9. September 2011 um 13:08 Uhr

  • @Alcott, verwenden Sie nicht memcpy, wenn Sie nicht wissen, dass sie sich nicht überlappen – verwenden Sie stattdessen memmove. Wenn es keine Überlappung gibt, sind memmove und memcpy gleichwertig (obwohl memcpy sehr, sehr, sehr geringfügig schneller sein kann).

    – bdonlan

    9. September 2011 um 21:33 Uhr

  • Sie können das Schlüsselwort „restrict“ verwenden, wenn Sie mit langen Arrays arbeiten und Ihren Kopiervorgang schützen möchten. Wenn Ihre Methode beispielsweise Eingabe- und Ausgabearrays als Parameter verwendet und Sie sicherstellen müssen, dass der Benutzer nicht dieselbe Adresse als Eingabe und Ausgabe übergibt. Lesen Sie mehr hier http://stackoverflow.com/questions/776283/…

    – DanielHsH

    1. Januar 2015 um 10:46 Uhr

  • @DanielHsH ‘restrict’ ist ein Versprechen, das Sie dem Compiler geben. es ist nicht durchgesetzt vom Compiler. Wenn Sie Ihre Argumente mit „restrict“ versehen und tatsächlich überlappen (oder allgemeiner auf die eingeschränkten Daten von Zeigern zugreifen, die von mehreren Stellen stammen), ist das Verhalten des Programms undefiniert, es treten seltsame Fehler auf, und der Compiler wird Sie normalerweise nicht davor warnen.

    – bdonlan

    9. Februar 2015 um 6:34 Uhr

  • @bdonlan Es ist nicht nur ein Versprechen an den Compiler, sondern eine Anforderung an Ihren Aufrufer. Es ist eine Anforderung, die nicht durchgesetzt wird, aber wenn Sie gegen eine Anforderung verstoßen, können Sie sich nicht beschweren, wenn Sie unerwartete Ergebnisse erhalten. Das Verletzen einer Anforderung ist ein undefiniertes Verhalten, genau wie i = i++ + 1 ist nicht definiert; Der Compiler verbietet Ihnen nicht, genau diesen Code zu schreiben, aber das Ergebnis dieser Anweisung kann alles sein, und verschiedene Compiler oder CPUs zeigen hier unterschiedliche Werte an.

    – Mecki

    14. April 2018 um 22:16 Uhr

Benutzeravatar von nos
Nr

memmove kann mit überlappendem Speicher umgehen, memcpy kippen.

In Betracht ziehen

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

Offensichtlich überschneiden sich jetzt Quelle und Ziel, wir überschreiben “-bar” mit “bar”. Es ist undefiniertes Verhalten mit memcpy wenn sich Quelle und Ziel überschneiden, so brauchen wir in diesem Fall Fälle memmove.

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

  • @ultraman: Weil es MÖGLICHERWEISE mit Low-Level-Assembly implementiert wurde, das erfordert, dass sich der Speicher nicht überlappt. Wenn dies der Fall ist, könnten Sie beispielsweise ein Signal oder eine Hardware-Ausnahme für den Prozessor generieren, der die Anwendung abbricht. Die Dokumentation gibt an, dass die Bedingung nicht behandelt wird, aber der Standard gibt nicht an, was passiert, wenn diese Bedingungen verletzt werden (dies wird als undefiniertes Verhalten bezeichnet). Undefiniertes Verhalten kann alles bewirken.

    – Martin York

    29. Juli 2009 um 17:21 Uhr

  • Mit gcc 4.8.2 akzeptiert sogar memcpy auch überlappende Quell- und Zielzeiger und funktioniert einwandfrei.

    – Jagdisch

    26. Juni 2015 um 2:18 Uhr

  • @jagsgediya Sicher könnte es. Aber da memcpy dokumentiert ist, dass es dies nicht unterstützt, sollten Sie sich nicht auf dieses implementierungsspezifische Verhalten verlassen, deshalb gibt es memmove(). In einer anderen Version von gcc kann es anders sein. Es könnte anders sein, wenn gcc memcpy einbettet, anstatt memcpy() in glibc aufzurufen, es könnte bei einer älteren oder neueren Version von glibc anders sein und so weiter.

    – Nr

    26. Juni 2015 um 8:32 Uhr


  • Aus der Praxis scheint es, dass memcpy und memmove dasselbe getan haben. So ein tiefes undefiniertes Verhalten.

    – Leben

    8. Februar 2016 um 22:52 Uhr

Von dem memcpy Manpage.

Die Funktion memcpy() kopiert n Bytes aus dem Speicherbereich src in den Speicherbereich dest. Die Speicherbereiche sollten sich nicht überschneiden. Verwenden memmove(3) wenn sich die Speicherbereiche überlappen.

Meckis Benutzeravatar
Mecki

Angenommen man müsste beides implementieren, könnte die Implementierung so aussehen:

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

Und das sollte den Unterschied ziemlich gut erklären. memmove kopiert immer so, dass es noch sicher ist, wenn src und dst überlappen, während memcpy kümmert sich einfach nicht darum, wie die Dokumentation bei der Verwendung sagt memcpydie beiden Speicherbereiche darf nicht Überlappung.

Bsp wenn memcpy kopiert “von vorne nach hinten” und die Speicherblöcke werden so ausgerichtet

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

Kopieren des ersten Bytes von src zu dst zerstört bereits den Inhalt der letzten Bytes von src bevor diese kopiert wurden. Nur das Kopieren „von hinten nach vorne“ führt zu korrekten Ergebnissen.

Jetzt tauschen src und dst:

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

In diesem Fall ist es nur sicher, “von vorne nach hinten” zu kopieren, da das Kopieren von “von hinten nach vorne” zerstören würde src nahe seiner Vorderseite bereits beim Kopieren des ersten Bytes.

Sie haben vielleicht bemerkt, dass die memmove Die obige Implementierung testet nicht einmal, ob sie sich tatsächlich überlappen, sondern prüft nur ihre relativen Positionen, aber das allein macht die Kopie sicher. Wie memcpy verwendet normalerweise den schnellstmöglichen Weg, um Speicher auf jedem System zu kopieren, memmove wird normalerweise eher implementiert als:

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

Manchmal, wenn memcpy kopiert immer “von vorne nach hinten” oder “von hinten nach vorne”, memmove darf auch verwendet werden memcpy in einem der sich überschneidenden Fälle aber memcpy kann sogar auf unterschiedliche Weise kopieren, je nachdem, wie die Daten ausgerichtet sind und / oder wie viele Daten kopiert werden sollen, also selbst wenn Sie getestet haben, wie memcpy Kopien auf Ihrem System befinden, können Sie sich nicht darauf verlassen, dass dieses Testergebnis immer korrekt ist.

Was bedeutet das für Sie bei der Entscheidung, welchen Sie anrufen?

  1. Es sei denn, Sie wissen das genau src und dst nicht überlappen, anrufen memmove da es immer zu korrekten Ergebnissen führt und in der Regel so schnell ist, wie es für den von Ihnen gewünschten Kopierfall möglich ist.

  2. Wenn Sie das sicher wissen src und dst nicht überlappen, anrufen memcpy da es keine Rolle spielt, welches Sie für das Ergebnis aufrufen, funktionieren beide in diesem Fall korrekt, aber memmove wird nie schneller sein als memcpy und wenn Sie Pech haben, kann es sogar langsamer sein, sodass Sie nur gewinnen können, wenn Sie mitgehen memcpy.

Benutzeravatar von Mirac Suzgun
Mirac Suzgun

Der Hauptunterschied zw memmove() und memcpy() ist das drin memmove() a Puffer – temporärer Speicher – verwendet wird, so dass keine Überschneidungsgefahr besteht. Auf der anderen Seite, memcpy() kopiert die Daten direkt von dem Speicherort, auf den der verweist Quelle zu dem Ort, auf den der zeigt Ziel. (http://www.cplusplus.com/reference/cstring/memcpy/)

Betrachten Sie die folgenden Beispiele:

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

    Wie erwartet wird Folgendes ausgedruckt:

    stackoverflow
    stackstacklow
    stackstacklow
    
  2. Aber in diesem Beispiel werden die Ergebnisse nicht dieselben sein:

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

    Ausgabe:

    stackoverflow
    stackstackovw
    stackstackstw
    

Dies liegt daran, dass “memcpy()” Folgendes tut:

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

  • Aber es scheint, dass die von Ihnen erwähnte Ausgabe umgekehrt ist !!

    – Kumar

    20. November 2014 um 16:15 Uhr


  • Wenn ich dasselbe Programm ausführe, erhalte ich das folgende Ergebnis: stackoverflow stackstackstw stackstackstw // bedeutet, dass es KEINEN Unterschied in der Ausgabe zwischen memcpy und memmove gibt

    – Kumar

    20. November 2014 um 16:16 Uhr

  • “dass in “memmove()” ein Puffer – ein temporärer Speicher – verwendet wird;” Ist nicht wahr. es sagt “als ob”, also muss es sich nur so verhalten, nicht dass es so sein muss. Das ist in der Tat relevant, da die meisten Memmove-Implementierungen nur einen XOR-Swap durchführen.

    – dhein

    8. Mai 2015 um 11:25 Uhr

  • Ich glaube nicht, die Umsetzung von memmove() ist erforderlich, um einen Puffer zu verwenden. Es ist vollkommen berechtigt, sich an Ort und Stelle zu bewegen (solange jeder Lesevorgang abgeschlossen ist, bevor an dieselbe Adresse geschrieben wird).

    – Toby Speight

    20. Mai 2016 um 10:54 Uhr

Benutzeravatar von CoffeeTableEspresso
KaffeetischEspresso

Einer (memmove) behandelt sich überschneidende Ziele der andere (memcpy) nicht.

  • Aber es scheint, dass die von Ihnen erwähnte Ausgabe umgekehrt ist !!

    – Kumar

    20. November 2014 um 16:15 Uhr


  • Wenn ich dasselbe Programm ausführe, erhalte ich das folgende Ergebnis: stackoverflow stackstackstw stackstackstw // bedeutet, dass es KEINEN Unterschied in der Ausgabe zwischen memcpy und memmove gibt

    – Kumar

    20. November 2014 um 16:16 Uhr

  • “dass in “memmove()” ein Puffer – ein temporärer Speicher – verwendet wird;” Ist nicht wahr. es sagt “als ob”, also muss es sich nur so verhalten, nicht dass es so sein muss. Das ist in der Tat relevant, da die meisten Memmove-Implementierungen nur einen XOR-Swap durchführen.

    – dhein

    8. Mai 2015 um 11:25 Uhr

  • Ich glaube nicht, die Umsetzung von memmove() ist erforderlich, um einen Puffer zu verwenden. Es ist vollkommen berechtigt, sich an Ort und Stelle zu bewegen (solange jeder Lesevorgang abgeschlossen ist, bevor an dieselbe Adresse geschrieben wird).

    – Toby Speight

    20. Mai 2016 um 10:54 Uhr

Benutzeravatar der Community
Gemeinschaft

einfach aus dem ISO/IEC:9899-Standard ist es gut beschrieben.

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

Und

7.21.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. Es findet ein Kopieren statt als ob die n Zeichen aus dem Objekt, auf das s2 zeigt, zuerst in ein temporäres Array von n Zeichen kopiert werden, das sich nicht überschneidet die Objekte, auf die s1 und s2 zeigen, und dann werden die n Zeichen aus dem temporären Array in das Objekt kopiert, auf das s1 zeigt.

Welche ich normalerweise je nach Frage verwende, hängt davon ab, welche Funktionalität ich benötige.

Im Klartext memcpy() erlaubt nicht s1 und s2 überlappen, während memmove() tut.

1423510cookie-checkWas ist der Unterschied zwischen memmove und memcpy?

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

Privacy policy