Quelle est la différence entre memmove
y memcpy
? Lequel utilisez-vous habituellement et comment ?
Réponses
Trop de publicités?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
pourrait toujours copier les 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
le détecterait et copierait dans l'autre sens - du haut vers le bas - dans ce cas. Cependant, vérifier cela et passer à un autre algorithme (éventuellement moins efficace) prend du temps.
memmove
peut gérer le chevauchement de la mémoire, memcpy
ne peut pas.
Pensez à
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 écrasons "-bar" avec "bar". C'est un comportement indéfini d'utiliser memcpy
si la source et la destination se chevauchent, donc dans ce cas, nous devons memmove
.
memmove(&str[3],&str[4],4); //fine
En supposant que vous deviez implémenter les deux, l'implémentation pourrait ressembler à cela :
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
}
}
Et ceci devrait assez bien expliquer la différence. memmove
toujours des copies de manière à ce qu'il soit toujours sûr si src
y dst
se chevauchent, tandis que memcpy
ne se préoccupe pas, comme le dit la documentation, de l'utilisation de memcpy
les deux zones de mémoire ne doit pas chevauchement.
Par exemple, si memcpy
copies "d'avant en arrière" et les blocs de mémoire sont alignés comme ceci
[---- src ----]
[---- dst ---]
en copiant le premier octet de src
a dst
détruit déjà le contenu des derniers octets de src
avant que ceux-ci n'aient été copiés. Seule la copie "d'arrière en avant" permet d'obtenir des résultats corrects.
Échangez maintenant src
y dst
:
[---- dst ----]
[---- src ---]
Dans ce cas, il est seulement prudent de copier "de l'avant à l'arrière" car copier "de l'arrière à l'avant" détruirait src
près de son avant déjà lors de la copie du premier octet.
Vous avez peut-être remarqué que le memmove
L'implémentation ci-dessus ne teste même pas s'ils se chevauchent réellement, elle vérifie juste leurs positions relatives, mais cela suffit à rendre la copie sûre. Comme memcpy
utilise généralement le moyen le plus rapide possible pour copier 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
copient toujours "l'avant vers l'arrière" ou "l'arrière vers l'avant", memmove
peut également utiliser memcpy
dans l'un des cas de chevauchement mais memcpy
peut même copier d'une manière différente selon la façon dont les données sont alignées et/ou la quantité de données à copier, donc même si vous avez testé la façon dont les memcpy
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 ?
-
A moins que vous ne soyez sûrs que
src
ydst
ne se chevauchent pas, appelezmemmove
car elle aboutit 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 êtes sûr que
src
ydst
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, de sorte que vous ne pouvez gagner qu'en appelantmemcpy
.
La principale différence entre memmove()
y memcpy()
est qu'en memmove()
a tampon - la mémoire temporaire - est utilisée, il n'y a donc aucun risque de chevauchement. D'autre part, memcpy()
copie directement les données à partir de l'emplacement pointé par la balise source à l'endroit indiqué par l'élément destination . ( http://www.cplusplus.com/reference/cstring/memcpy/ )
Prenons 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 s'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; }
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
- Réponses précédentes
- Plus de réponses