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
136
demandé sur Jonathan Leffler 2010-12-11 11:34:41

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).

106
répondu developmentalinsanity 2018-04-02 09:42:01

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.

80
répondu rxantos 2018-05-06 08:45:03

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.

31
répondu Billy ONeal 2010-12-11 08:38:32

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
15
répondu Neilvert Noval 2013-06-07 19:44:28

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.

7
répondu huubby 2013-11-07 03:13:39

la différence entre memcpy et memmove est que

  1. 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.

  2. 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
3
répondu R srinivas reddy 2014-10-06 09:35:31

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.

2
répondu Огњен Шобајић 2016-04-19 23:41:21

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;

1
répondu rockeet 2013-06-21 04:18:44

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.

1
répondu singingsingh 2014-03-30 15:55:41

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,

  1. aabbcc -> aaabcc

  2. aaabcc -> aaaacc

  3. aaaacc -> aaaaac

  4. aaaaac -> aaaaaa

memmove() copie les données à la variable temporaire d'abord, puis copie à l'emplacement de la mémoire réelle.

  1. aabbcc(actual) -> aabbcc(temp)

  2. aabbcc(temp) -> aaabcc(act)

  3. aabbcc(temp) -> aaaacc(act)

  4. aabbcc(temp) -> aaaabc(act)

  5. aabbcc(temp) -> aaaabb(act)

Sortie

memcpy : aaaaaa

memmove : aaaabb

-3
répondu Pratik Panchal 2015-01-05 05:50:57