Quelle est la différence entre memmove
et memcpy
? Lequel utilisez-vous habituellement et comment?
Quelle est la différence entre memmove
et memcpy
? Lequel utilisez-vous habituellement et comment?
Réponses:
Avec memcpy
, la destination ne peut pas du tout chevaucher la source. Avec memmove
ça peut. Cela signifie que cela memmove
pourrait être très légèrement plus lent quememcpy
, car il ne peut pas faire les mêmes hypothèses.
Par exemple, memcpy
peut toujours copier les adresses de bas en haut. Si la destination chevauche après la source, cela signifie que certaines adresses seront écrasées avant d'être copiées. memmove
détecterait cela et copier dans l'autre sens - de haut en bas - dans ce cas. Cependant, vérifier cela et passer à un autre algorithme (peut-être moins efficace) prend du temps.
i = i++ + 1
n'est pas défini; le compilateur ne vous interdit pas d'écrire exactement ce code mais le résultat de cette instruction peut être n'importe quoi et différents compilateurs ou processeurs afficheront ici des valeurs différentes.
memmove
peut gérer la mémoire qui se chevauchent, memcpy
ne peut pas.
Considérer
char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up
De toute évidence, la source et la destination se chevauchent maintenant, nous remplaçons "-bar" par "bar". C'est un comportement indéfini en utilisant memcpy
si la source et la destination se chevauchent, donc dans ce cas, nous en avons besoin memmove
.
memmove(&str[3],&str[4],4); //fine
La principale différence entre memmove()
et memcpy()
est que dans memmove()
un tampon - la mémoire temporaire - est utilisé, il n'y a donc aucun risque de chevauchement. D'un autre côté, memcpy()
copie directement les données de l'emplacement pointé par la source vers l'emplacement pointé par la destination . ( http://www.cplusplus.com/reference/cstring/memcpy/ )
Considérez 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, ceci imprimera:
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;
}
Production:
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
memmove()
soit nécessaire pour utiliser un tampon. Il est parfaitement autorisé à se déplacer sur place (tant que chaque lecture se termine avant toute écriture à la même adresse).
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 manière à ce qu'il soit toujours en sécurité si src
et se dst
chevauchent, alors memcpy
que ce n'est pas grave comme le dit la documentation lors de l'utilisation memcpy
, les deux zones de mémoire ne doivent pas chevaucher.
Par exemple, si les memcpy
copies «recto verso» et les blocs de mémoire sont alignés comme ceci
[---- src ----]
[---- dst ---]
la copie du premier octet de src
à dst
détruit déjà le contenu des derniers octets desrc
avant qu'ils aient été copiés. Seule la copie «recto verso» donnera des résultats corrects.
Maintenant échangez src
et dst
:
[---- dst ----]
[---- src ---]
Dans ce cas, il est prudent de copier uniquement «recto verso» car la copie «recto verso» détruirait src
déjà près de son devant lors de la copie du premier octet.
Vous avez peut-être remarqué que l' memmove
implémentation ci-dessus ne teste même pas si elles se chevauchent réellement, elle vérifie simplement leurs positions relatives, mais cela seul rendra la copie sûre. Comme il memcpy
utilise généralement le moyen le plus rapide possible de copier de la mémoire sur n'importe quel système, il memmove
est généralement implémenté comme suit:
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, s'il memcpy
copie toujours «recto verso» ou «recto verso», il memmove
peut également être utilisé 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 à être copié, donc même si vous avez testé commentmemcpy
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 lorsque vous décidez lequel appeler?
À moins que vous savez que vous src
et dst
ne se chevauchent pas, l' appel memmove
comme il sera toujours conduire à des résultats corrects et est généralement aussi vite que cela est possible dans le cas de copie dont vous avez besoin.
Si vous le savez avec certitude src
et que vous dst
ne vous chevauchez pas, appelez memcpy
car peu importe celui que vous appelez pour le résultat, les deux fonctionneront correctement dans ce cas, mais memmove
ne seront jamais plus rapides memcpy
et si vous êtes malchanceux, cela peut même être plus lent, vous ne pouvez donc gagner que l'appel memcpy
.
simplement à partir de la norme ISO / CEI: 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 l'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 l'objet pointé par s1. La copie a lieu comme si les n caractères de l'objet pointé par s2 étaient d'abord copiés dans un tableau temporaire de n caractères qui ne se chevauchent pas les objets pointés par s1 et s2, puis les n caractères du tableau temporaire sont copiés dans l'objet pointé par s1.
Lequel j'utilise habituellement selon la question, dépend de la fonctionnalité dont j'ai besoin.
En texte brut, memcpy()
ne permet pas s1
et s2
de se chevaucher, alors que le memmove()
fait.
Il existe 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 procède des adresses basse vers haute, et dans la seconde, de haute vers basse. Si la plage à copier se chevauche (comme c'est le cas lors du défilement d'un framebuffer, par exemple), alors un seul sens de fonctionnement est correct, et l'autre écrasera les emplacements qui seront ensuite lus.
Une memmove()
implémentation, dans sa forme la 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 faire cela bien sûr, car même après la conversion src
et dst
vers un type de pointeur concret, ils ne pointent pas (en général) vers 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 de comportement indéfini.
Notez que dans la vraie vie, les implémentations ont tendance à être beaucoup plus complexes, pour obtenir les performances maximales des 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 le plus simplement possible.
memmove peut gérer le chevauchement des régions source et destination, contrairement à memcpy. 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 Cain, (Stanford Intro Systems Lecture - 7) Heure: 36:00
memcpy()
et non memcopy()
.