J'essaie de comprendre la différence entre memcpy()
et memmove()
, et j'ai lu le texte qui memcpy()
ne prend pas en compte la source et la destination qui se chevauchent alors que le memmove()
fait.
Cependant, lorsque j'exécute ces deux fonctions sur des blocs de mémoire qui se chevauchent, elles donnent toutes deux le même résultat. Par exemple, prenez l'exemple MSDN suivant sur la memmove()
page d'aide: -
Existe-t-il un meilleur exemple pour comprendre les inconvénients memcpy
et comment les memmove
résoudre?
// 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: %s\n", str1 );
memcpy( str1 + 2, str1, 4 );
printf( "New string: %s\n", str1 );
strcpy_s( str1, sizeof(str1), "aabbcc" ); // reset string
printf( "The string: %s\n", str1 );
memmove( str1 + 2, str1, 4 );
printf( "New string: %s\n", str1 );
}
Production:
The string: aabbcc
New string: aaaabb
The string: aabbcc
New string: aaaabb
memcpy
- fort voudraitassert
que les régions ne se chevauchent pas plutôt que de masquer intentionnellement les bogues dans votre code.The string: aabbcc New string: aaaaaa The string: aabbcc New string: aaaabb
Réponses:
Je ne suis pas entièrement surpris que votre exemple ne présente aucun comportement étrange. Essayez plutôt
str1
de copierstr1+2
et voyez ce qui se passe ensuite. (Peut ne pas faire de différence, dépend du compilateur / des bibliothèques.)En général, memcpy est implémenté de manière simple (mais rapide). De manière simpliste, il ne fait que faire une boucle sur les données (dans l'ordre), en copiant d'un emplacement à l'autre. Cela peut entraîner l'écrasement de la source lors de sa lecture.
Memmove fait plus de travail pour s'assurer qu'il gère correctement le chevauchement.
ÉDITER:
(Malheureusement, je ne peux pas trouver d'exemples décents, mais ceux-ci feront l'affaire). Comparez les implémentations memcpy et memmove illustrées ici. memcpy ne fait que faire une boucle, tandis que memmove effectue un test pour déterminer dans quelle direction effectuer la boucle pour éviter de corrompre les données. Ces implémentations sont assez simples. La plupart des implémentations hautes performances sont plus compliquées (impliquant la copie de blocs de taille de mot à la fois plutôt que d'octets).
la source
memmove
appellememcpy
dans une branche après avoir testé les pointeurs: student.cs.uwaterloo.ca/~cs350/common/os161-src-htmlmemcpy
peut être plus rapide.La mémoire dans
memcpy
ne peut pas se chevaucher ou vous risquez un comportement indéfini, tandis que la mémoire dansmemmove
peut se chevaucher.Certaines implémentations de memcpy peuvent toujours fonctionner pour des entrées qui se chevauchent, mais vous ne pouvez pas compter ce comportement. Alors que memmove doit permettre le chevauchement.
la source
Ce
memcpy
n'est pas parce qu'il n'a pas à faire face à des régions qui se chevauchent que cela ne signifie pas qu'il les traite correctement. L'appel avec des régions qui se chevauchent produit un comportement indéfini. Un comportement non défini peut fonctionner entièrement comme prévu sur une seule plateforme; cela ne veut pas dire que c'est correct ou valide.la source
memcpy
soit implémentée exactement de la même manière quememmove
. Autrement dit, celui qui a écrit le compilateur n'a pas pris la peine d'écrire unememcpy
fonction unique .Memcpy et memove font des choses similaires.
Mais pour voir une différence:
donne:
la source
Votre démo n'a pas exposé les inconvénients de memcpy à cause d'un "mauvais" compilateur, elle vous rend service dans la version Debug. Une version finale, cependant, vous donne le même résultat, mais à cause de l'optimisation.
Le registre
%eax
joue ici comme un stockage temporaire, qui corrige "élégamment" le problème de chevauchement.L'inconvénient apparaît lors de la copie de 6 octets, au moins une partie de celui-ci.
Production:
Ça a l'air bizarre, c'est aussi causé par l'optimisation.
C'est pourquoi je choisis toujours
memmove
en essayant de copier 2 blocs de mémoire superposés.la source
La différence entre
memcpy
etmemmove
est quedans
memmove
, la mémoire source de la taille spécifiée est copiée dans la mémoire tampon, puis déplacée vers la destination. Donc, si la mémoire se chevauche, il n'y a pas d'effets secondaires.dans le cas de
memcpy()
, aucun tampon supplémentaire n'est utilisé pour la mémoire source. La copie se fait directement sur la mémoire afin qu'en cas de chevauchement de mémoire, nous obtenions des résultats inattendus.Ceux-ci peuvent être observés par le code suivant:
La sortie est:
la source
Comme déjà souligné dans d'autres réponses,
memmove
est plus sophistiqué quememcpy
tel qu'il tient compte des chevauchements de mémoire. Le résultat de memmove est défini comme si lesrc
était copié dans un tampon, puis copié dans le tampondst
. Cela ne signifie PAS que l'implémentation réelle utilise un tampon, mais fait probablement une arithmétique de pointeur.la source
compilateur pourrait optimiser memcpy, par exemple:
Ce memcpy peut être optimisé comme:
x = *(int*)some_pointer;
la source
int
accès non alignés . Sur certaines architectures (par exemple Cortex-M0), tenter de récupérer un 32 bits àint
partir d'une adresse qui n'est pas un multiple de quatre provoquera un crash (maismemcpy
fonctionnera). Si l'on utilise un processeur qui permet un accès non aligné ou un compilateur avec un mot-clé qui dirige le compilateur pour assembler des entiers à partir d'octets extraits séparément si nécessaire, on pourrait faire quelque chose comme#define UNALIGNED __unaligned
et puis `x = * (int UNALIGNED * ) some_pointer;char x = "12345"; int *i; i = *(int *)(x + 1);
mais certains le font, car ils corrigent la copie pendant la panne. J'ai travaillé sur un système comme celui-ci et il m'a fallu un peu de temps pour comprendre pourquoi les performances étaient si mauvaises.*(int *)some_pointer
est une violation stricte d'aliasing, mais vous voulez probablement dire que le compilateur afficherait l'assembly qui copie un intLe code donné dans les liens http://clc-wiki.net/wiki/memcpy pour memcpy semble me dérouter un peu, car il ne donne pas le même résultat lorsque je l'ai implémenté en utilisant l'exemple ci-dessous.
Production :
Mais vous pouvez maintenant comprendre pourquoi memmove s'occupera du problème de chevauchement.
la source
Projet standard C11
Le projet standard C11 N1570 dit:
7.24.2.1 "La fonction memcpy":
7.24.2.2 "La fonction memmove":
Par conséquent, tout chevauchement
memcpy
conduit à un comportement indéfini, et tout peut arriver: mauvais, rien ou même bien. Le bien est rare cependant :-)memmove
cependant dit clairement que tout se passe comme si un tampon intermédiaire est utilisé, donc clairement les chevauchements sont OK.std::copy
Cependant, C ++ est plus indulgent et autorise les chevauchements: std :: copy gère-t-il les plages qui se chevauchent?la source
memmove
utilise un tableau temporaire supplémentaire de n, donc utilise-t-il de la mémoire supplémentaire? Mais comment le faire si nous ne lui avons donné accès à aucune mémoire. (Il utilise 2x la mémoire).J'ai essayé d'exécuter le même programme en utilisant eclipse et cela montre une différence claire entre
memcpy
etmemmove
.memcpy()
ne se soucie pas du chevauchement de l'emplacement de la mémoire qui entraîne la corruption des données, alors quememmove()
copiera d'abord les données dans une variable temporaire, puis copiera dans l'emplacement de mémoire réel.Lors de la tentative de copie des données de l'emplacement
str1
versstr1+2
, la sortie dememcpy
est "aaaaaa
". La question serait comment?memcpy()
copiera un octet à la fois de gauche à droite. Comme indiqué dans votre programme "aabbcc
", toutes les copies auront lieu comme ci-dessous,aabbcc -> aaabcc
aaabcc -> aaaacc
aaaacc -> aaaaac
aaaaac -> aaaaaa
memmove()
copiera d'abord les données dans la variable temporaire, puis copiera dans l'emplacement de mémoire réel.aabbcc(actual) -> aabbcc(temp)
aabbcc(temp) -> aaabcc(act)
aabbcc(temp) -> aaaacc(act)
aabbcc(temp) -> aaaabc(act)
aabbcc(temp) -> aaaabb(act)
La sortie est
memcpy
:aaaaaa
memmove
:aaaabb
la source
memmove()
copies vers un emplacement intermédiaire. Il doit simplement copier à l'envers si nécessaire.