Quelle est la différence entre memmove et memcpy?

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

94
demandé sur Community 2009-07-29 20:01:42

11 réponses

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 peut toujours copier des 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 détecterait cela et copierait dans l'autre sens - de haut en bas - dans ce cas. Cependant, en vérifiant cela et passer à un autre algorithme (peut-être moins efficace) prend du temps.

134
répondu bdonlan 2015-06-19 12:31:28

memmove peut gérer la mémoire qui se chevauchent, memcpy ne peut pas.

Considérez

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 indéfini en utilisant memcpy si la source et la destination se chevauchent donc dans ce cas, nous avons besoin de memmove.

memmove(&str[3],&str[4],4); //fine
28
répondu nos 2013-04-22 15:57:14

De la page de manuelmemcpy .

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.

21
répondu John Carter 2009-07-29 16:02:41

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

Considérons 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 imprimera sortie:

    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
11
répondu Mirac Suzgun 2016-05-20 10:57:55

L'un gère les destinations qui se chevauchent, l'autre Non.

9
répondu KPexEA 2009-07-29 16:02:37

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 le 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 objet pointé par s1. La copie a lieu comme si les N caractères de l'objet pointés par s2 sont d'abord copiés dans un tableau temporaire de n caractères qui ne superposer {[13] } les objets pointés par s1 et s2, puis les N caractères du les tableaux temporaires sont copiés dans l'objet pointé par s1.

Lequel j'utilise habituellement selon la question, dépend de quelle fonctionnalité Je besoin.

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

5
répondu dhein 2015-05-08 10:48:32

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 assez bien expliquer la différence. memmove copie toujours de telle manière, qu'il est toujours sûr si src et dst se chevauchent, alors que memcpy ne s'en soucie pas comme le dit la documentation lors de l'utilisation de memcpy, les deux zones de mémoire ne doivent pas se chevaucher.

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

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

Alors copier le 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 "back to front".

Maintenant échanger src et dst:

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

Dans ce cas, il est seulement sûr de copier "front to back" car copier "back to front" détruirait src près de son front déjà lors de la copie du premier octet.

Vous avez peut-être remarqué que l'implémentation memmove ci-dessus ne teste même pas si elles le font réellement chevauchement, il vérifie simplement leurs positions relatives, mais cela seul rendra la copie sûre. Comme memcpy utilise généralement le moyen le plus rapide possible de copier de 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 copie toujours "recto-verso" ou "recto-verso", {[4] } 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 la façon dont les données sont alignées et / ou de la quantité de données à copier, donc même si vous avez testé memcpy copies 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 quand vous décidez lequel appeler?

  1. Sauf si vous savez avec certitude que src et dst ne se chevauchent pas, appelez memmove car cela conduira 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 savez avec certitude que src et 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, donc vous ne pouvez gagner que l'appel memcpy.

2
répondu Mecki 2018-08-20 09:34:37

J'ai essayé d'exécuter le code ci-dessus: raison principale pour laquelle je veux connaître la différence de 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 ci-dessous la sortie

stackoverflow
stackstackovw
stackstackstw

Maintenant, j'ai remplacé memmove par memcpy et j'ai eu la même sortie

#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
répondu sobin thomas 2015-03-22 17:57:39

Char chaîne [] = "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 c'est-à-dire "stackov" collé à + 5 Position 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 seconde (c'est-à-dire "stackst") est copié dans un tampon, puis collé à l'endroit de +5 résultant

O / p-stackstackstw

0-----+5
pile stackst w

0
répondu chetan h 2016-05-20 10:32:53

Il y a deux façons é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 la première implémentation, la copie passe des adresses basses aux adresses hautes, et dans la Seconde, des adresses hautes aux adresses basses. Si la plage à copier se chevauche (comme c'est le cas lors du défilement d'un framebuffer, par exemple), une seule direction de fonctionnement est correcte, et l'autre écrasera les emplacements qui seront lus par la suite.

Un memmove() l'implémentation, à sa plus simple, testera dest<src (d'une 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 jeté src et dst sur un type de pointeur concret, ils ne pointent pas (en général) dans le même objet et ne peuvent donc pas être comparés. Mais la bibliothèque standard peut avoir suffisamment de connaissances sur la plate-forme pour effectuer une telle comparaison sans provoquer un comportement indéfini.


Notez que dans la vraie vie, les implémentations ont tendance à être beaucoup plus complexes, pour obtenir les performances maximales de 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.

0
répondu Toby Speight 2016-05-30 15:18:05

Memmove peut gérer les régions source et destination qui se chevauchent, alors que memcpy ne le peut pas. Parmi les deux, memcpy est beaucoup plus efficace. Donc, mieux vaut utiliser memcpy si vous le pouvez.

Référence: https://www.youtube.com/watch?v=Yr1YnOVG-4g Dr Jerry Caïn, (Stanford Intro Systèmes de Conférence - 7) Durée: 36:00

0
répondu Ehsan 2016-12-10 03:17:10