memcpy () vs memmove()
j'essaie de comprendre la différence entre memcpy()
et memmove()
, et j'ai lu le texte que memcpy()
ne s'occupe pas de la source et de la destination qui se chevauchent alors que memmove()
le fait.
cependant, quand j'exécute ces deux fonctions sur des blocs de mémoire qui se chevauchent, elles donnent toutes les deux le même résultat. Par exemple, prenez L'exemple MSDN suivant sur la page d'aide memmove()
:-
y a-t-il un meilleur exemple pour comprendre les inconvénients de memcpy
et comment memmove
résout-il?
// 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: %sn", str1 );
memcpy( str1 + 2, str1, 4 );
printf( "New string: %sn", str1 );
strcpy_s( str1, sizeof(str1), "aabbcc" ); // reset string
printf( "The string: %sn", str1 );
memmove( str1 + 2, str1, 4 );
printf( "New string: %sn", str1 );
}
sortie:
The string: aabbcc
New string: aaaabb
The string: aabbcc
New string: aaaabb
10 réponses
Je ne suis pas entièrement surpris que votre exemple ne montre aucun comportement étrange. Essayez plutôt de copier str1
en str1+2
et voyez ce qui se passe ensuite. (Ne peut pas réellement faire une différence, dépend du compilateur/bibliothèques.)
en général, memcpy est mis en œuvre d'une manière simple (mais rapide). De manière simpliste, il ne fait que boucler les données (dans l'ordre), en les copiant d'un endroit à l'autre. Cela peut avoir pour résultat que la source soit écrasée pendant qu'elle est lue.
Memmove fait plus de travail pour s'assurer qu'il gère le chevauchement correctement.
EDIT:
(malheureusement, je ne trouve pas d'exemples décents, mais ceux-ci feront l'affaire). Comparer les implémentations memcpy et memmove ici. memcpy ne fait que des boucles, tandis que memmove effectue un test pour déterminer dans quelle direction boucler pour éviter de corrompre les données. Ces implémentations sont assez simples. La plupart des implémentations haute performance sont plus compliquées (impliquant la copie de blocs de taille word à la fois plutôt que des octets).
La mémoire memcpy
ne peut pas "151950920 de" chevauchement ou vous risquez de comportement indéfini, alors que la mémoire dans memmove
peuvent se chevaucher.
char a[16];
char b[16];
memcpy(a,b,16); // valid
memmove(a,b,16); // Also valid, but slower than memcpy.
memcpy(&a[0], &a[1],10); // Not valid since it overlaps.
memmove(&a[0], &a[1],10); // valid.
certaines implémentations de memcpy peuvent toujours fonctionner pour des entrées qui se chevauchent, mais vous ne pouvez pas compter ce comportement. Tandis que memmove doit permettre le chevauchement.
ce n'est pas parce que memcpy
n'a pas à traiter avec les régions qui se chevauchent, que cela ne veut pas dire qu'il ne traite pas avec eux correctement. L'appel avec des régions qui se chevauchent produit un comportement non défini. Un comportement non défini peut fonctionner entièrement comme vous l'attendez sur une plate-forme; cela ne signifie pas qu'il est correct ou valide.
memcpy et memove font des choses similaires.
mais à voir une différence:
#include <memory.h>
#include <string.h>
#include <stdio.h>
char str1[17] = "abcdef";
int main()
{
printf( "The string: %s\n", str1 );
memcpy( (str1+6), str1, 10 );
printf( "New string: %s\n", str1 );
strcpy_s( str1, sizeof(str1), "aabbcc" ); // reset string
printf( "The string: %s\n", str1 );
memmove( (str1+6), str1, 10 );
printf( "New string: %s\n", str1 );
}
donne:
The string: abcdef
New string: abcdefabcdefabcd
The string: abcdef
New string: abcdefabcdef
votre démo n'a pas exposé les inconvénients de memcpy à cause de" mauvais " compilateur, il vous fait une faveur dans la version de débogage. Une version de publication, cependant, vous donne la même sortie, mais à cause de l'optimisation.
memcpy(str1 + 2, str1, 4);
00241013 mov eax,dword ptr [str1 (243018h)] // load 4 bytes from source string
printf("New string: %s\n", str1);
00241018 push offset str1 (243018h)
0024101D push offset string "New string: %s\n" (242104h)
00241022 mov dword ptr [str1+2 (24301Ah)],eax // put 4 bytes to destination
00241027 call esi
Le registre %eax
joue ici comme un stockage temporaire, qui "élégamment" correctifs de problème de chevauchement.
l'inconvénient émerge quand on copie 6 octets, enfin, au moins une partie de celui-ci.
char str1[9] = "aabbccdd";
int main( void )
{
printf("The string: %s\n", str1);
memcpy(str1 + 2, str1, 6);
printf("New string: %s\n", str1);
strcpy_s(str1, sizeof(str1), "aabbccdd"); // reset string
printf("The string: %s\n", str1);
memmove(str1 + 2, str1, 6);
printf("New string: %s\n", str1);
}
sortie:
The string: aabbccdd
New string: aaaabbbb
The string: aabbccdd
New string: aaaabbcc
semble bizarre, il est causé par l'optimisation, aussi.
memcpy(str1 + 2, str1, 6);
00341013 mov eax,dword ptr [str1 (343018h)]
00341018 mov dword ptr [str1+2 (34301Ah)],eax // put 4 bytes to destination, earlier than the above example
0034101D mov cx,word ptr [str1+4 (34301Ch)] // HA, new register! Holding a word, which is exactly the left 2 bytes (after 4 bytes loaded to %eax)
printf("New string: %s\n", str1);
00341024 push offset str1 (343018h)
00341029 push offset string "New string: %s\n" (342104h)
0034102E mov word ptr [str1+6 (34301Eh)],cx // Again, pulling the stored word back from the new register
00341035 call esi
C'est pourquoi je choisis toujours memmove
en essayant de copier 2 blocs de mémoire superposés.
la différence entre memcpy
et memmove
est que
-
dans
memmove
, la mémoire source de taille spécifiée est copiée dans un tampon puis déplacée à destination. Donc, si la mémoire se chevauchent, il n'y a pas d'effets secondaires. -
dans le cas de
memcpy()
, il n'y a pas de tampon supplémentaire pour la mémoire source. La copie se fait directement sur la mémoire de sorte que lorsqu'il y a chevauchement de mémoire, résultats inattendus.
ceux-ci peuvent être observés par le code suivant:
//include string.h, stdio.h, stdlib.h
int main(){
char a[]="hare rama hare rama";
char b[]="hare rama hare rama";
memmove(a+5,a,20);
puts(a);
memcpy(b+5,b,20);
puts(b);
}
sortie:
hare hare rama hare rama
hare hare hare hare hare hare rama hare rama
comme il a déjà été souligné dans d'autres réponses, memmove
est plus sophistiqué que memcpy
de sorte qu'il tient compte des chevauchements de mémoire. Le résultat de memmove est défini comme si le src
était copié dans un tampon puis copié dans dst
. Cela ne signifie pas que la mise en œuvre réelle utilise un tampon, mais fait probablement une certaine arithmétique pointeur.
compilateur pourrait optimiser memcpy, par exemple:
int x;
memcpy(&x, some_pointer, sizeof(int));
cette memcpy peut être optimisée comme: x = *(int*)some_pointer;
le code indiqué dans les liens http://clc-wiki.net/wiki/memcpy pour memcpy semble m'embrouiller un peu, car il ne donne pas la même sortie lorsque je l'ai implémenté en utilisant l'exemple ci-dessous.
#include <memory.h>
#include <string.h>
#include <stdio.h>
char str1[11] = "abcdefghij";
void *memcpyCustom(void *dest, const void *src, size_t n)
{
char *dp = (char *)dest;
const char *sp = (char *)src;
while (n--)
*dp++ = *sp++;
return dest;
}
void *memmoveCustom(void *dest, const void *src, size_t n)
{
unsigned char *pd = (unsigned char *)dest;
const unsigned char *ps = (unsigned char *)src;
if ( ps < pd )
for (pd += n, ps += n; n--;)
*--pd = *--ps;
else
while(n--)
*pd++ = *ps++;
return dest;
}
int main( void )
{
printf( "The string: %s\n", str1 );
memcpy( str1 + 1, str1, 9 );
printf( "Actual memcpy output: %s\n", str1 );
strcpy_s( str1, sizeof(str1), "abcdefghij" ); // reset string
memcpyCustom( str1 + 1, str1, 9 );
printf( "Implemented memcpy output: %s\n", str1 );
strcpy_s( str1, sizeof(str1), "abcdefghij" ); // reset string
memmoveCustom( str1 + 1, str1, 9 );
printf( "Implemented memmove output: %s\n", str1 );
getchar();
}
sortie:
The string: abcdefghij
Actual memcpy output: aabcdefghi
Implemented memcpy output: aaaaaaaaaa
Implemented memmove output: aabcdefghi
mais vous pouvez maintenant comprendre pourquoi memmove va prendre soin de la question de chevauchement.
j'ai essayé d'exécuter le même programme en utilisant eclipse et il montre une nette différence entre memcpy
et memmove
. memcpy()
ne se soucie pas de chevauchement de l'emplacement de mémoire qui entraîne la corruption des données, tandis que memmove()
va copier les données à la variable temporaire d'abord et ensuite copier dans l'emplacement de mémoire réelle.
en essayant de copier des données de l'emplacement str1
à str1+2
, la sortie de memcpy
est aaaaaa
". Question serait comment?
memcpy()
copie d'un octet à la fois de gauche à droite. Comme indiqué dans votre programme " aabbcc
" alors
toute copie aura lieu comme ci-dessous,
-
aabbcc -> aaabcc
-
aaabcc -> aaaacc
-
aaaacc -> aaaaac
-
aaaaac -> aaaaaa
memmove()
copie les données à la variable temporaire d'abord, puis copie à l'emplacement de la mémoire réelle.
-
aabbcc(actual) -> aabbcc(temp)
-
aabbcc(temp) -> aaabcc(act)
-
aabbcc(temp) -> aaaacc(act)
-
aabbcc(temp) -> aaaabc(act)
-
aabbcc(temp) -> aaaabb(act)
Sortie
memcpy
: aaaaaa
memmove
: aaaabb