Was ist der Unterschied zwischen memmove
und memcpy
? Welches verwendest du normalerweise und wie?
Was ist der Unterschied zwischen memmove und memcpy?
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 memcpy
da 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
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.
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 memcpy
die 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?
-
Es sei denn, Sie wissen das genau
src
unddst
nicht überlappen, anrufenmemmove
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. -
Wenn Sie das sicher wissen
src
unddst
nicht überlappen, anrufenmemcpy
da es keine Rolle spielt, welches Sie für das Ergebnis aufrufen, funktionieren beide in diesem Fall korrekt, abermemmove
wird nie schneller sein alsmemcpy
und wenn Sie Pech haben, kann es sogar langsamer sein, sodass Sie nur gewinnen können, wenn Sie mitgehenmemcpy
.
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:
-
#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
-
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
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
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.
Beachten Sie mögliche Probleme: lwn.net/Articles/414467
– Zan Luchs
22. April 2013 um 16:33 Uhr