Ich versuche, den Unterschied zwischen memcpy()
und zu verstehen memmove()
, und ich habe den Text gelesen, der memcpy()
sich nicht um die überlappende Quelle und das überlappende Ziel memmove()
kümmert.
Wenn ich diese beiden Funktionen jedoch auf überlappenden Speicherblöcken ausführe, ergeben 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 es memmove
gelöst werden kann?
// 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
memcpy
wäre,assert
dass sich die Regionen nicht überlappen, anstatt absichtlich Fehler in Ihrem Code zu vertuschen.The string: aabbcc New string: aaaaaa The string: aabbcc New string: aaaabb
Antworten:
Ich bin nicht ganz überrascht, dass Ihr Beispiel kein seltsames Verhalten zeigt. Versuchen Sie das Kopieren
str1
zustr1+2
statt und sehen , was dann passiert. (Kann keinen Unterschied machen, hängt vom Compiler / den Bibliotheken ab.)Im Allgemeinen wird memcpy auf einfache (aber schnelle) Weise implementiert. Vereinfacht gesagt werden nur die Daten (in der angegebenen 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 behandelt wird.
BEARBEITEN:
(Leider kann ich keine anständigen Beispiele finden, aber diese reichen aus). Vergleichen Sie die memcpy und memmove Implementierungen hier gezeigt. memcpy wird nur wiederholt, während memmove einen Test durchführt, um festzustellen, in welche Richtung eine Schleife ausgeführt werden soll, um eine Beschädigung der Daten zu vermeiden. Diese Implementierungen sind ziemlich einfach. Die meisten Hochleistungsimplementierungen sind komplizierter (das gleichzeitige Kopieren von Blöcken in Wortgröße anstelle von Bytes).
quelle
memmove
ruftmemcpy
in einem Zweig nach dem Zeiger Prüfung: student.cs.uwaterloo.ca/~cs350/common/os161-src-html/...memcpy
schneller sein kann.Der Speicher in
memcpy
kann sich nicht überlappen, oder Sie riskieren undefiniertes Verhalten, während sich der Speicher inmemmove
überlappen kann.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.
quelle
Nur weil
memcpy
es sich nicht um überlappende Regionen handeln muss, heißt das nicht, dass es nicht richtig mit ihnen umgeht. Der Aufruf mit überlappenden Regionen erzeugt undefiniertes Verhalten. Undefiniertes Verhalten kann auf einer Plattform wie erwartet funktionieren. das heißt nicht, dass es richtig oder gültig ist.quelle
memcpy
genau so implementiert wird wiememmove
. Das heißt, wer auch immer den Compiler geschrieben hat, hat sich nicht die Mühe gemacht, eine eindeutigememcpy
Funktion zu schreiben .Sowohl memcpy als auch memove machen ähnliche Dinge.
Aber um einen Unterschied zu erkennen:
gibt:
quelle
Ihre Demo hat keine memcpy-Nachteile aufgrund des "schlechten" Compilers aufgedeckt, es tut Ihnen in der Debug-Version einen Gefallen. Eine Release-Version liefert jedoch die gleiche Ausgabe, jedoch aufgrund von Optimierungen.
Das Register wird
%eax
hier als temporärer Speicher abgespielt, wodurch Überlappungsprobleme "elegant" behoben werden.Der Nachteil tritt auf, wenn 6 Bytes kopiert werden, zumindest ein Teil davon.
Ausgabe:
Sieht komisch aus, es wird auch durch Optimierung verursacht.
Deshalb wähle ich immer,
memmove
wenn ich versuche, 2 überlappende Speicherblöcke zu kopieren.quelle
Der Unterschied zwischen
memcpy
undmemmove
ist dasIn
memmove
wird der Quellspeicher der angegebenen Größe in den Puffer kopiert und dann zum Ziel verschoben. Wenn sich der Speicher also überlappt, treten keine Nebenwirkungen auf.Im Falle von
memcpy()
wird kein zusätzlicher Puffer für den Quellspeicher benötigt. Das Kopieren erfolgt direkt im Speicher, sodass bei Überlappung des Speichers unerwartete Ergebnisse erzielt werden.Diese können durch den folgenden Code beobachtet werden:
Ausgabe ist:
quelle
Wie bereits in anderen Antworten erwähnt,
memmove
ist es komplexer alsmemcpy
solche, dass es Speicherüberschneidungen berücksichtigt. Das Ergebnis von memmove ist so definiert, als ob dassrc
in einen Puffer und dann in den Puffer kopiert wurdedst
. Dies bedeutet NICHT, dass die eigentliche Implementierung einen Puffer verwendet, sondern wahrscheinlich eine Zeigerarithmetik.quelle
Der Compiler könnte memcpy optimieren, zum Beispiel:
Dieser Speicher kann wie folgt optimiert werden:
x = *(int*)some_pointer;
quelle
int
Zugriffe ermöglichen. Bei einigen Architekturen (z. B. Cortex-M0) führt der Versuch, ein 32-Bitint
von einer Adresse abzurufen, die kein Vielfaches von vier ist, zu einem Absturz (memcpy
würde aber funktionieren). Wenn man entweder eine CPU verwendet, die einen nicht ausgerichteten Zugriff ermöglicht, oder einen Compiler mit einem Schlüsselwort verwendet, das den Compiler#define UNALIGNED __unaligned
anweist, bei Bedarf Ganzzahlen aus separat abgerufenen Bytes zusammenzusetzen, könnte man so etwas tun und dann `x = * (int UNALIGNED * ) some_pointer;char x = "12345"; int *i; i = *(int *)(x + 1);
andere jedoch, weil sie die Kopie während des Fehlers reparieren. Ich habe an einem solchen System gearbeitet, und es hat einige Zeit gedauert, um zu verstehen, warum die Leistung so schlecht war.*(int *)some_pointer
ist eine strikte Aliasing-Verletzung, aber Sie meinen wahrscheinlich, dass der Compiler eine Assembly ausgeben würde, die eine intDer Code in den Links http://clc-wiki.net/wiki/memcpy für memcpy scheint mich ein wenig zu verwirren, da er nicht die gleiche Ausgabe liefert, wenn ich ihn anhand des folgenden Beispiels implementiert habe.
Ausgabe :
Aber Sie können jetzt verstehen, warum memmove sich um überlappende Probleme kümmert.
quelle
C11 Standardentwurf
Der Standardentwurf C11 N1570 lautet:
7.24.2.1 "Die memcpy-Funktion":
7.24.2.2 "Die memmove-Funktion":
Daher führt jede Überlappung
memcpy
zu undefiniertem Verhalten, und alles kann passieren: schlecht, nichts oder sogar gut. Gut ist allerdings selten :-)memmove
Es wird jedoch klar gesagt, dass alles so geschieht, als ob ein Zwischenpuffer verwendet wird, sodass Überlappungen eindeutig in Ordnung sind.C ++
std::copy
ist jedoch verzeihender und erlaubt Überlappungen: Behandelt std :: copy überlappende Bereiche?quelle
memmove
Verwenden Sie ein zusätzliches temporäres Array von n. Verwendet es also zusätzlichen Speicher? Aber wie kann es sein, wenn wir ihm keinen Zugriff auf einen Speicher gewährt haben? (Es verwendet 2x den Speicher).Ich habe versucht, dasselbe Programm mit Eclipse auszuführen, und es zeigt einen deutlichen Unterschied zwischen
memcpy
undmemmove
.memcpy()
Es ist nicht wichtig, dass sich der Speicherort überlappt, was zu einer Beschädigung der Daten führt, während zuerstmemmove()
Daten in eine temporäre Variable und dann in den tatsächlichen Speicherort kopiert werden.Beim Versuch, Daten vom Speicherort
str1
nach zu kopierenstr1+2
, ist die Ausgabe vonmemcpy
"aaaaaa
". Die Frage wäre wie?memcpy()
kopiert jeweils ein Byte von links nach rechts. Wie in Ihrem Programm "aabbcc
" gezeigt, erfolgt der gesamte Kopiervorgang wie folgt:aabbcc -> aaabcc
aaabcc -> aaaacc
aaaacc -> aaaaac
aaaaac -> aaaaaa
memmove()
kopiert zuerst Daten in eine temporäre Variable und dann in den tatsächlichen Speicherort.aabbcc(actual) -> aabbcc(temp)
aabbcc(temp) -> aaabcc(act)
aabbcc(temp) -> aaaacc(act)
aabbcc(temp) -> aaaabc(act)
aabbcc(temp) -> aaaabb(act)
Ausgabe ist
memcpy
::aaaaaa
memmove
::aaaabb
quelle
memmove()
Kopien an einen Zwischenort kopiert werden. Es sollte bei Bedarf nur in umgekehrter Reihenfolge kopiert werden.