Quelle est la différence entre memmove et memcpy?
Quelle est la différence entre memmove
et memcpy
? Lequel utilisez-vous habituellement et comment?
11 réponses
Avec memcpy
, la destination ne peut pas du tout chevaucher la source. Avec memmove
Il peut. Cela signifie que memmove
pourrait être très légèrement plus lent que memcpy
, car il ne peut pas faire les mêmes hypothèses.
Par exemple, memcpy
peut toujours copier des adresses de bas en haut. Si la destination se chevauche après la source, cela signifie que certaines adresses seront écrasées avant d'être copiées. memmove
détecterait cela et copierait dans l'autre sens - de haut en bas - dans ce cas. Cependant, en vérifiant cela et passer à un autre algorithme (peut-être moins efficace) prend du temps.
memmove
peut gérer la mémoire qui se chevauchent, memcpy
ne peut pas.
Considérez
char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up
Évidemment, la source et la destination se chevauchent maintenant, nous écrasons
"-bar" avec "bar". C'est un comportement indéfini en utilisant memcpy
si la source
et la destination se chevauchent donc dans ce cas, nous avons besoin de memmove
.
memmove(&str[3],&str[4],4); //fine
De la page de manuelmemcpy .
La fonction memcpy () copie n octets de la zone mémoire src à la zone mémoire dest. Les zones de mémoire ne doivent pas chevauchement. Utilisez memmove (3) si la mémoire les zones se chevauchent.
La principale différence entre memmove()
et memcpy()
est que dans memmove()
un tampon - mémoire temporaire - est utilisé, donc il n'y a pas de risque de chevauchement. D'autre part, memcpy()
directement copie les données à partir de l'emplacement pointé par la source à l'endroit pointé par la destination. (http://www.cplusplus.com/reference/cstring/memcpy/)
Considérons les exemples suivants:
-
#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; }
Comme vous vous y attendiez, cela imprimera sortie:
stackoverflow stackstacklow stackstacklow
-
, Mais dans cet exemple, les résultats ne seront pas les mêmes:
#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; }
Sortie:
stackoverflow stackstackovw stackstackstw
C'est parce que" memcpy () " fait ce qui suit:
1. stackoverflow
2. stacksverflow
3. stacksterflow
4. stackstarflow
5. stackstacflow
6. stackstacklow
7. stackstacksow
8. stackstackstw
Simplement à partir de la norme ISO / IEC:9899, il est bien décrit.
7.21.2.1 la fonction memcpy
[...]
2 la fonction memcpy copie n caractères de l'objet pointé par s2 dans le objet pointé par s1. si la copie a lieu entre des objets qui se chevauchent, le comportement n'est pas défini.
Et
7.21.2.2 la fonction memmove
[...]
2 la fonction memmove copie n caractères de l'objet pointé par s2 dans le objet pointé par s1. La copie a lieu comme si les N caractères de l'objet pointés par s2 sont d'abord copiés dans un tableau temporaire de n caractères qui ne superposer {[13] } les objets pointés par s1 et s2, puis les N caractères du les tableaux temporaires sont copiés dans l'objet pointé par s1.
Lequel j'utilise habituellement selon la question, dépend de quelle fonctionnalité Je besoin.
En texte brut memcpy()
ne permet pas s1
et s2
de se chevaucher, alors que memmove()
le fait.
En supposant que vous deviez implémenter les deux, l'implémentation pourrait ressembler à ceci:
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
}
}
Et cela devrait assez bien expliquer la différence. memmove
copie toujours de telle manière, qu'il est toujours sûr si src
et dst
se chevauchent, alors que memcpy
ne s'en soucie pas comme le dit la documentation lors de l'utilisation de memcpy
, les deux zones de mémoire ne doivent pas se chevaucher.
Par exemple, si memcpy
copie "de l'avant vers l'arrière" et que les blocs de mémoire sont alignés comme ceci
[---- src ----]
[---- dst ---]
Alors copier le premier octet de src
do dst
détruit déjà le contenu des derniers octets de src
avant que ceux-ci aient été copiés. Donc, ici, il est seulement sûr de copier "back to front".
Maintenant échanger src
et dst
:
[---- dst ----]
[---- src ---]
Dans ce cas, il est seulement sûr de copier "front to back" car copier "back to front" détruirait src
près de son front déjà lors de la copie du premier octet.
Vous avez peut-être remarqué que l'implémentation memmove
ci-dessus ne teste même pas si elles le font réellement chevauchement, il vérifie simplement leurs positions relatives, mais cela seul rendra la copie sûre. Comme memcpy
utilise généralement le moyen le plus rapide possible de copier de la mémoire sur n'importe quel système, memmove
est généralement plutôt implémenté comme:
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);
}
}
Parfois, si memcpy
copie toujours "recto-verso" ou "recto-verso", {[4] } peut également utiliser memcpy
dans l'un des cas de chevauchement, mais memcpy
peut même copier d'une manière différente en fonction de la façon dont les données sont alignées et / ou de la quantité de données à copier, donc même si vous avez testé memcpy
copies sur votre système, vous ne pouvez pas compter sur ce résultat de test pour être toujours correct.
Qu'est-ce que cela signifie pour vous quand vous décidez lequel appeler?
Sauf si vous savez avec certitude que
src
etdst
ne se chevauchent pas, appelezmemmove
car cela conduira toujours à des résultats corrects et est généralement aussi rapide que possible pour le cas de copie dont vous avez besoin.Si vous savez avec certitude que
src
etdst
ne se chevauchent pas, appelezmemcpy
car peu importe lequel vous appelez pour le résultat, les deux fonctionneront correctement dans ce cas, maismemmove
ne sera jamais plus rapide quememcpy
et si vous êtes malchanceux, il peut même être plus lent, donc vous ne pouvez gagner que l'appelmemcpy
.
J'ai essayé d'exécuter le code ci-dessus: raison principale pour laquelle je veux connaître la différence de memcpy
et memmove
.
#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;
}
Donne ci-dessous la sortie
stackoverflow
stackstackovw
stackstackstw
Maintenant, j'ai remplacé memmove
par memcpy
et j'ai eu la même sortie
#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,7);
puts(first);
memcpy(second+5, second, 7);
puts(second);
return 0;
}
Sortie:
stackoverflow
stackstackovw
stackstackstw
Char chaîne [] = "stackoverflow";
char *first, *second;
first = string;
second = string;
puts(string);
O / P-stackoverflow
memcpy(first+5, first,7);
puts(first);
Ici 7 caractères pointés par la seconde c'est-à-dire "stackov" collé à + 5 Position résultant
O / p-stackstackovw
memcpy(second+5, second, 7);
puts(second);
Ici, la chaîne d'entrée est "stackstackovw", 7 caractères pointés par seconde (c'est-à-dire "stackst") est copié dans un tampon, puis collé à l'endroit de +5 résultant
O / p-stackstackstw
0-----+5
pile stackst w
Il y a deux façons évidentes d'implémenter mempcpy(void *dest, const void *src, size_t n)
(en ignorant la valeur de retour):
for (char *p=src, *q=dest; n-->0; ++p, ++q) *q=*p;
char *p=src, *q=dest; while (n-->0) q[n]=p[n];
Dans la première implémentation, la copie passe des adresses basses aux adresses hautes, et dans la Seconde, des adresses hautes aux adresses basses. Si la plage à copier se chevauche (comme c'est le cas lors du défilement d'un framebuffer, par exemple), une seule direction de fonctionnement est correcte, et l'autre écrasera les emplacements qui seront lus par la suite.
Un memmove()
l'implémentation, à sa plus simple, testera dest<src
(d'une manière dépendante de la plate-forme) et exécutera la direction appropriée de memcpy()
.
Le code utilisateur ne peut pas le faire bien sûr, car même après avoir jeté src
et dst
sur un type de pointeur concret, ils ne pointent pas (en général) dans le même objet et ne peuvent donc pas être comparés. Mais la bibliothèque standard peut avoir suffisamment de connaissances sur la plate-forme pour effectuer une telle comparaison sans provoquer un comportement indéfini.
Notez que dans la vraie vie, les implémentations ont tendance à être beaucoup plus complexes, pour obtenir les performances maximales de transferts plus importants (lorsque l'alignement le permet) et/ou une bonne utilisation du cache de données. Le code ci-dessus est juste pour faire le point aussi simplement que possible.
Memmove peut gérer les régions source et destination qui se chevauchent, alors que memcpy ne le peut pas. Parmi les deux, memcpy est beaucoup plus efficace. Donc, mieux vaut utiliser memcpy si vous le pouvez.
Référence: https://www.youtube.com/watch?v=Yr1YnOVG-4g Dr Jerry Caïn, (Stanford Intro Systèmes de Conférence - 7) Durée: 36:00