138 votes

Quelle est la différence entre memmove et memcpy ?

Quelle est la différence entre memmove y memcpy ? Lequel utilisez-vous habituellement et comment ?

176voto

bdonlan Points 90068

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.

38voto

nos Points 102226

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

24voto

Mecki Points 35351

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 ?

  1. A moins que vous ne soyez sûrs que src y dst ne se chevauchent pas, appelez memmove 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.

  2. Si vous êtes sûr que src y dst ne se chevauchent pas, appelez memcpy car peu importe lequel vous appelez pour le résultat, les deux fonctionneront correctement dans ce cas, mais memmove ne sera jamais plus rapide que memcpy et si vous êtes malchanceux, il peut même être plus lent, de sorte que vous ne pouvez gagner qu'en appelant memcpy .

23voto

therefromhere Points 21329

De la memcpy page de manuel.

La fonction memcpy() copie n octets de la zone de mémoire src à la zone de mémoire dest. Les zones de mémoire ne doivent pas se chevaucher. Utilisez memmove (3) si la mémoire zones de mémoire se chevauchent.

12voto

suzgunmirac Points 11

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 :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X