web-dev-qa-db-fra.com

Quelle est la difference entre memmove et memcpy?

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

102
ultraman

Avec memcpy, la destination ne peut pas chevaucher la source du tout. Avec memmove c'est possible. 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 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 la copie. memmove détecterait ceci 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.

147
bdonlan

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

Considérer 

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". Il s'agit d'un comportement non défini utilisant memcpy si la source Et la destination se chevauchent. Dans ce cas, nous avons besoin de memmove.

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

Depuis la page de manuel memcpy .

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

21
John Carter

La principale différence entre memmove() et memcpy() est que, dans memmove(), un buffer - mémoire temporaire - est utilisé. Il n'y a donc aucun risque de chevauchement. D'autre part, memcpy() copie directement les données de l'emplacement désigné par source vers l'emplacement désigné par 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 ()" a les effets suivants: 

1.  stackoverflow
2.  stacksverflow
3.  stacksterflow
4.  stackstarflow
5.  stackstacflow
6.  stackstacklow
7.  stackstacksow
8.  stackstackstw
11
Mirac Suzgun

L'une gère les destinations qui se chevauchent, l'autre pas.

9
KPexEA

simplement 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 le fichier 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 le fichier objet pointé par s1. La copie a lieu comme si les n caractères de l’objet s2 sont tout d’abord copiés dans un tableau temporaire de n caractères qui ne le fait pas overlap les objets pointés par s1 et s2, puis les n caractères du tableau temporaire sont copiés dans l'objet pointé par s1.

Lequel j’utilise habituellement selon la question dépend de la fonctionnalité dont j’ai besoin.

En texte brut, memcpy() ne permet pas le chevauchement de s1 et s2, contrairement à memmove().

5
dhein

En supposant que vous ayez à 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 fait toujours des copies de telle sorte qu'il soit toujours sûr que src et dst se chevauchent, alors que memcpy s'en fiche, comme le dit la documentation, lorsque vous utilisez memcpy, les deux zones de mémoire ne doivent pas se chevauchent.

Par exemple. si memcpy copie "d'avant en arrière" et que les blocs de mémoire sont alignés comme ceci

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

Ensuite, la copie du premier octet de src do dst détruit déjà le contenu des derniers octets de src avant que ceux-ci aient été copiés. Donc, ici, il est seulement sûr de copier "de haut en bas". 

Maintenant, permutez src et dst:

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

Dans ce cas, il est seulement sûr de copier "d'avant en arrière", car copier "d'avant en arrière" détruirait déjà src près de son devant lors de la copie du premier octet.

Vous avez peut-être remarqué que l'implémentation memmove 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 memcpy utilise généralement le moyen le plus rapide possible pour copier de la mémoire sur n’importe quel système, memmove 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 memcpy copie toujours "avant à l'arrière" ou "arrière à 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 en fonction de l'alignement des données et/ou de l'ampleur les données doivent être copiées. Ainsi, même si vous avez testé la manière dont memcpy copies sur votre système, vous ne pouvez pas compter sur le 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 savez avec certitude que src et dst ne se chevauchent pas, appelez memmove car cela donnera toujours des résultats corrects et est généralement aussi rapide que possible pour le casier dont vous avez besoin.

  2. Si vous savez avec certitude que src et dst ne se chevauchent pas, appelez memcpy car peu importe celui que 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, vous ne pouvez donc gagner que si vous appelez memcpy.

4
Mecki

memmove peut traiter les régions source et cible qui se chevauchent, alors que memcpy ne le peut pas. Parmi les deux, memcpy est beaucoup plus efficace. Donc, mieux vaut l’utiliser, 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

0
Ehsan

chaîne de caractères [] = "stackoverflow";

char *first, *second;

first = string;

second = string;

puts(string);

o/p - stackoverflow

memcpy(first+5, first,7);

puts(first);

Ici 7 caractères pointés par la seconde, "stackov" collés à la position +5 résultant 

o/p - stackstackovw

memcpy(second+5, second, 7);

puts(second);

ici la chaîne d'entrée est "stackstackovw", 7 caractères pointés par une seconde (c'est-à-dire "stackst") sont copiés dans un tampon, puis collés à la place du +5 résultant 

o/p - stackstackstw

0 ----- + 5
stack stackst w

0
chetan h

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

  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 le premier mode de réalisation, la copie passe des adresses basses aux adresses hautes et, dans le second, des adresses hautes à basses. Si la plage à copier se superpose (comme c'est le cas lors du défilement d'un framebuffer, par exemple), une seule direction est correcte et l'autre remplace les emplacements à partir desquels vous lirez.

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

Bien entendu, le code utilisateur ne peut pas le faire, car même après avoir jeté src et dst dans 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. Toutefois, la bibliothèque standard peut disposer de suffisamment de connaissances sur la plate-forme pour effectuer une telle comparaison sans provoquer de comportement indéfini.


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

0
Toby Speight

J'ai essayé de lancer le code ci-dessus: Raison principale pour laquelle je souhaite connaître la différence entre memcpy et memmove.

#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;
}

donne en dessous de la sortie

stackoverflow
stackstackovw
stackstackstw

Maintenant, j'ai remplacé memmove par memcpy et j'ai le même résultat

#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,7);

    puts(first);

    memcpy(second+5, second, 7);

    puts(second);

    return 0;
}

Sortie:

stackoverflow
stackstackovw
stackstackstw
0
sobin thomas