Was ist der Unterschied zwischen memmove und Memcpy?
Frage
Was ist der Unterschied zwischen memmove
und memcpy
? Welches verwenden Sie in der Regel und wie?
Lösung
Mit memcpy
kann das Ziel nicht die Quelle überlappt überhaupt. Mit memmove
kann es. Das bedeutet, dass memmove
sehr sein könnte etwas langsamer als memcpy
, da es nicht die gleichen Annahmen getroffen werden kann.
Zum Beispiel memcpy
könnte immer kopieren Adressen von niedrig bis hoch. Wenn das Ziel nach der Quelle überlappt, bedeutet dies, einige Adressen überschrieben werden, bevor kopiert. memmove
würden diese, und kopieren Sie in der anderen Richtung erkennt - von hoch zu niedrig - in diesem Fall. Allerdings überprüft diese und auf einem anderen (möglicherweise weniger effizient) Algorithmus Schalt braucht Zeit.
Andere Tipps
memmove
können überlappende Speicher umgehen, memcpy
kann es nicht.
Betrachten
char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up
Offensichtlich ist die Quelle und das Ziel jetzt überlappen, wir überschreiben
"-Bar" mit "bar". Es ist nicht definiertes Verhalten mit memcpy
, wenn die Quelle
und Ziel-Überlappung so in diesem Fall Fällen brauchen wir memmove
.
memmove(&str[3],&str[4],4); //fine
Der Hauptunterschied zwischen memmove()
und memcpy()
ist, dass in memmove()
a Puffer - Zwischenspeicher - verwendet wird, so dass es keine Gefahr von Überschneidungen. Auf der anderen Seite, memcpy()
direkt kopiert die Daten von der Stelle, die von der Ziel , auf die der source auf den Ort gerichtet ist. ( http://www.cplusplus.com/reference/cstring/memcpy/ )
Beachten 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 Sie erwarten, wird dies ausdrucken:
stackoverflow stackstacklow stackstacklow
-
Aber in diesem Beispiel werden die Ergebnisse nicht gleich 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
Es ist, weil "memcpy ()" führt Folgendes aus:
1. stackoverflow
2. stacksverflow
3. stacksterflow
4. stackstarflow
5. stackstacflow
6. stackstacklow
7. stackstacksow
8. stackstackstw
Eine Griff überlappende Ziele der andere nicht.
einfach von der ISO / IEC. 9899 Standard es gut beschrieben wird,
7.21.2.1 Die Memcpy Funktion
[...]
2 Die Memcpy Funktion kopiert n Zeichen aus dem Objekt, auf dem durch s2 in die Objekt, auf das durch s1. Wenn das Kopieren zwischen Objekten erfolgt, die sich überlappen, ist das Verhalten nicht definiert.
Und
7.21.2.2 Die memmove Funktion
[...]
2 Die memmove Funktion kopiert n Zeichen aus dem Objekt in die von s2 hingewiesen Objekt, auf das durch s1. Das Kopieren erfolgt als ob die n Zeichen aus dem Objekt s2, auf den zunächst in eine temporäre Matrix von n Zeichen kopiert, die nicht der Fall ist überlappen wiesen die Objekte von s1 und s2, und dann die n Zeichen aus der temporäres Array kopiert werden in das Objekt von s1 hingewiesen.
Was man in der Regel ich auf die Frage Acording verwenden, hängt davon ab, welche functionallity ich brauche.
Im Klartext memcpy()
nicht erlaubt s1
und s2
zu überlappen, während memmove()
der Fall ist.
Angenommen, Sie würden beide implementieren, die Umsetzung so aussehen könnte:
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
}
}
Und das sollte ziemlich gut den Unterschied erklären. memmove
immer Kopien so, dass es immer noch sicher ist, wenn src
und dst
Überlappung, während memcpy
gerade interessiert sich nicht wie die Dokumentation sagt, wenn memcpy
verwenden, die zwei Speicherbereiche muss nicht überlappen.
z. wenn memcpy
Kopien „von vorne nach hinten“ und die Speicherblöcke sind als diese ausgerichtet
[---- src ----]
[---- dst ---]
das erste Byte src
Kopieren zerstört dst
bereits den Inhalt des letzten Bytes src
bevor diese kopiert wurde. Nur „hinten nach vorne“ zu kopieren, um korrekten Ergebnissen führen wird.
Jetzt tauschen src
und dst
:
[---- dst ----]
[---- src ---]
In diesem Fall ist es nur sicher zu kopieren, wie das Kopieren "Front to Back" "hinten nach vorne" würde src
in der Nähe seiner Vorderseite zerstört bereits, wenn das erste Byte zu kopieren.
Sie haben vielleicht bemerkt, dass die memmove
Implementierung oben noch nicht testen, ob sie tatsächlich überlappen, sie ihre relativen Positionen nur überprüft, aber das allein wird die Kopie sicher machen. Als memcpy
in der Regel auf den schnellsten Weg nutzt den Hauptspeicher auf jedem System zu kopieren, wird memmove
in der Regel eher umgesetzt 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
immer Kopien „von vorne nach hinten“ oder „hinten nach vorne“, memmove
auch memcpy
in eines der überlappenden Fällen verwenden können, aber memcpy
können kopieren, auch in einer anderen Art und Weise, je nachdem wie die Daten ausgerichtet ist und / oder wie viele Daten kopiert werden, so dass selbst wenn Sie getestet, wie memcpy
Kopien auf Ihrem System, können Sie nicht auf diesem Testergebnis verlassen immer korrekt zu sein.
Was bedeutet das für Sie bei der Entscheidung, die man anrufen?
-
Wenn Sie nicht sicher wissen, dass
src
unddst
sich nicht überlappen, ruftmemmove
wie es immer Ergebnisse führen zu korrigieren und ist in der Regel so schnell wie die für die Kopie Fall möglich ist, die Sie benötigen. -
Wenn Sie sicher wissen, dass
src
unddst
nicht überlappen, Callmemcpy
als es keine Rolle, wird, welche Sie für das Ergebnis nennen, die beide in diesem Fall korrekt funktionieren, abermemmove
nie schneller alsmemcpy
und noch langsamer kann es sein, wenn man Pech hat, kann man nur so nennenmemcpy
gewinnen.
Es gibt zwei offensichtliche Wege mempcpy(void *dest, const void *src, size_t n)
(ohne Berücksichtigung der Rückgabewert) zu implementieren:
-
for (char *p=src, *q=dest; n-->0; ++p, ++q) *q=*p;
-
char *p=src, *q=dest; while (n-->0) q[n]=p[n];
In der ersten Implementierung, die Kopie Erlös von niedrigen zu hohen Adressen und in der zweiten, von hoch zu niedrig. Wenn der Bereich Überschneidungen kopiert wird (wie es der Fall ist, wenn ein Framebuffer, zum Beispiel Scrollen), dann wird nur eine Arbeitsrichtung richtig ist, und die andere überschreiben Orte, die anschließend aus gelesen werden.
A memmove()
Implementierung Im einfachsten wird, testen dest<src
(in einiger plattformabhängigen Art und Weise), und die entsprechende Richtung des memcpy()
auszuführen.
Code Der Benutzer kann das natürlich nicht tun, denn auch nach src
und dst
bis zu einem gewissen konkreten Zeigerart Gießen, sie nicht (im Allgemeinen) in das gleiche Objekt zeigen und so nicht miteinander verglichen werden können. Aber die Standard-Bibliothek kann genug Plattform Wissen haben einen solchen Vergleich durchzuführen, ohne nicht definiertes Verhalten zu verursachen.
Beachten Sie, dass im wirklichen Leben, Implementierungen sind in der Regel wesentlich komplexer sein, um die maximale Leistung aus größeren Transfers zu gewinnen (wenn die Ausrichtung zulässt) und / oder gute Daten-Cache-Nutzung. Der obige Code ist nur um den Punkt zu machen so einfach wie möglich.
memmove kann mit überlappenden Quell- und Zielregionen beschäftigen, während Memcpy nicht. Unter den beiden ist Memcpy wesentlich effizienter. Also, besser nutzen es MEMCPY wenn Sie können.
Referenz: https://www.youtube.com/watch?v=Yr1YnOVG -4G Dr. Jerry Cain (Stanford Intro Systeme Vortrag - 7) Zeit: 36:00