Question

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

Était-ce utile?

La solution

Avec memcpy, la destination ne peut en aucun cas chevaucher la source. Avec memmove c'est possible. Cela signifie que <=> peut être très légèrement plus lent que <=>, car il ne peut pas émettre les mêmes hypothèses.

Par exemple, <=> peut toujours copier les adresses de bas en haut. Si la destination se superpose à la source, cela signifie que certaines adresses seront écrasées avant d'être copiées. <=> détecterait cela et copierait dans l'autre sens - de haut en bas - dans ce cas. Cependant, vérifier cela et passer à un autre algorithme (peut-être moins efficace) prend du temps.

Autres conseils

memmove peut gérer le chevauchement de la mémoire, memcpy ne le peut pas.

Envisagez

char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up

Évidemment, la source et la destination se chevauchent maintenant, nous écrasons " -bar " avec " bar " ;. C’est un comportement non défini en utilisant <=> si la source et la destination se chevauchent donc dans ce cas nous avons besoin de <=>.

memmove(&str[3],&str[4],4); //fine

À partir de la memcpy page

.
  

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

La principale différence entre memmove() et memcpy() est que dans <=> un tampon - mémoire temporaire - est utilisé, il n'y a donc aucun risque de chevauchement. D'autre part, <=> copie directement les données de l'emplacement désigné par la source vers l'emplacement indiqué par la destination . ( http://www.cplusplus.com/reference/cstring/memcpy/ )

Considérez 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, ceci affichera:

    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

L'un traite les destinations qui se chevauchent, l'autre pas.

simplement à partir de la norme ISO/IEC:9899, ​​il est bien décrit.

7.21.2.1 La fonction memcpy

[...]

2 La fonction MEMCPY copie n caractères de l'objet pointé par S2 dans l'objet pointé par S1. Si la copie a lieu entre des objets qui se chevauchent, le comportement n'est pas défini.

Et

7.21.2.2 La fonction memmove

[...]

2 La fonction MemMove copie n caractères de l'objet pointé par S2 dans l'objet pointé par S1.La copie a lieu comme si les n caractères de l'objet pointés par S2 étaient d'abord copiés dans un tableau temporaire de n caractères qui ne chevauche pas Les objets pointés par S1 et S2, puis les N les caractères du tableau temporaire sont copiés dans l'objet pointé par S1.

Celui que j'utilise habituellement en fonction de la question dépend de la fonctionnalité dont j'ai besoin.

En texte brut memcpy() ne permet pas s1 et s2 se chevaucher, tandis que memmove() fait.

En supposant que vous deviez implémenter les deux, l'implémentation pourrait ressembler à ceci:

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 mempy ( void * dst, const void * src, size_t count ) {
    if ((uintptr_t)src != (uintptr_t)dst) {
        // Copy in any way you want
    }
}

Et cela devrait plutôt bien expliquer la différence. memmove copie toujours de manière à ce qu'il soit toujours sûr de se chevaucher src et dst, alors que memcpy s'en fiche, comme le dit la documentation lorsque vous utilisez <=>, les deux zones de mémoire ne doit pas se chevaucher.

E.g. si <=> copie " recto à verso " et les blocs de mémoire sont alignés comme ceci

[---- src ----]
            [---- dst ---]

copier le premier octet de <=> dans <=> détruit déjà le contenu des derniers octets de <=> avant que ceux-ci aient été copiés. Copier uniquement & Quot; de haut en bas & Quot; conduira à corriger les résultats.

Maintenant, échangez <=> et <=>:

[---- dst ----]
            [---- src ---]

Dans ce cas, il est seulement prudent de copier " d'avant en arrière " Copier " de haut en bas " détruirait <=> déjà près de son front lors de la copie du premier octet.

Vous avez peut-être remarqué que la <=> mise en œuvre ci-dessus ne vérifie même pas si elles se chevauchent réellement, elle vérifie simplement leurs positions relatives, mais cela seul permettra de sécuriser la copie. Comme <=> utilise généralement le moyen le plus rapide possible pour copier de la mémoire sur n’importe quel système, <=> est généralement implémenté comme suit:

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 <=> copie toujours " recto à verso " ou " back to front " ;, <=> peut également utiliser <=> dans l'un des cas de chevauchement, mais <=> peut même copier d'une manière différente en fonction de l'alignement des données et / ou combien de données doivent être copiées. Ainsi, même si vous avez testé la méthode de <=> copie sur votre système, vous ne pouvez pas vous fier au résultat de ce test pour qu'il soit toujours correct.

Qu'est-ce que cela signifie pour vous lorsque vous décidez lequel appeler?

  1. Sauf si vous êtes certain que <=> et <=> ne se chevauchent pas, appelez <=>, car les résultats seront toujours corrects et le plus rapidement possible pour le cas de copie que vous avez sélectionné. besoin.

  2. Si vous savez avec certitude que <=> et <=> ne se chevauchent pas, appelez <=>, car le choix du résultat n'a aucune importance, les deux fonctionneront correctement dans ce cas, mais <=> ne sera jamais plus rapide que <=> et si vous n’êtes pas chanceux, il risque même d’être plus lent. Vous ne pouvez donc gagner que si vous appelez <=>.

Il existe deux manières évidentes d'implémenter mempcpy(void *dest, const void *src, size_t n) (en ignorant la valeur renvoyée):

  1. for (char *p=src, *q=dest;  n-->0;  ++p, ++q)
        *q=*p;
    
  2. char *p=src, *q=dest;
    while (n-->0)
        q[n]=p[n];
    

Dans la première implémentation, la copie passe des adresses basses aux adresses hautes et, dans la seconde, des adresses hautes à basses. Si la plage à copier se chevauche (comme c'est le cas lors du défilement d'un framebuffer, par exemple), une seule direction est correcte et l'autre remplacera les emplacements qui seront lus ultérieurement.

Une implémentation memmove(), dans sa forme la plus simple, testera dest<src (de manière dépendante de la plate-forme) et exécutera la direction appropriée de memcpy().

Le code utilisateur ne peut pas le faire bien sûr, car même après avoir converti src et dst en un type de pointeur concret, ils ne pointent (en général) dans le même objet et ne peuvent donc pas être comparés. . Cependant, la bibliothèque standard peut disposer de suffisamment de connaissances en matière de plate-forme pour effectuer une telle comparaison sans provoquer de comportement indéfini.

Notez que, dans la réalité, les implémentations ont tendance à être considérablement plus complexes, afin d’obtenir les meilleures performances possibles grâce à des transferts plus importants (lorsque l’alignement le permet) et / ou à une bonne utilisation du cache de données. Le code ci-dessus est juste pour faire le point aussi simplement que possible.

memmove peut traiter les régions source et cible qui se chevauchent, alors que memcpy ne le peut pas. Parmi les deux, la mémoire est beaucoup plus efficace. Donc, mieux vaut utiliser la mémoire si vous le pouvez.

Référence: https://www.youtube.com/watch?v=Yr1YnOVG -4g Dr. Jerry Cain, (Conférence de Stanford Intro Systems - 7) Heure: 36:00

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top