Différence entre memcpy et memmove

Résolu
leoliom Messages postés 187 Statut Membre -  
leoliom Messages postés 187 Statut Membre -
Bonjour, j’ai lu beaucoup de documents ,j’ai regardé pleins d’exemples mais impossible de comprendre les trucs de chevauchement et autre .Je veux comprend de sorte que je peux prévoir ce que le programme va m’afficher

Configuration: iPad / Chrome 98.0.4758.97

9 réponses

  1. jeannets Messages postés 28408 Date d'inscription   Statut Contributeur Dernière intervention   Ambassadeur 6 605
     
    Bonjour,

    Memcpy est une copie de mémoire sur une autre adresse, sans détruire l'original

    https://man7.org/linux/man-pages/man3/memcpy.3.html

    Tandis que Memmove est un déplacement d'une mémoire, Contrairement à Memcpy, il efface la source..

    https://www.man7.org/linux/man-pages/man3/memmove.3.html

    Les attribues définissent la source, la destination et la taille des valeurs déplacées ou copiées...

    Bien sur pour que tout soit synchro, il faut connaitre sa leçon et les valeurs en mouvements. faire des exemples sur du papier quadrillé..
    0
  2. leoliom Messages postés 187 Statut Membre 2
     
    J’ai lu aussi ce site mais j’ai pas compris.Comme vous le dites le seul moyen de comprendre c’est par un schéma sauf que moi je sais pas j’ai du mal

    Peut été un petit exemple me permettra de mieux comprendre
    0
  3. Dalfab Messages postés 638 Date d'inscription   Statut Membre Dernière intervention   102
     
    Les 2 fonctions ne se distinguent que dans le cas où il y a recouvrement. C'est quand des cases sont à la fois dans la source à copier et dans la destination.
    Exemple tu veux copier les 4 premières cases du tableau suivant dans les 4 dernières:
    +---+---+---+---+---+---+---+
    | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
    +---+---+---+---+---+---+---+

    Si tu utilises
    memmove()
    tu obtiendras le résultat suivant (les 4 dernières reçoivent bien ce qu'il y avait dans les 4 premières)
    +---+---+---+---+---+---+---+
    | 1 | 2 | 3 | 1 | 2 | 3 | 4 |
    +---+---+---+---+---+---+---+

    Si tu utilises
    memcpy()
    , cette fonction est un peu "bête", elle va vite mais ne "réfléchit" pas beaucoup! Elle risque de te donner le résultat suivant:
    +---+---+---+---+---+---+---+
    | 1 | 2 | 3 | 1 | 2 | 3 | 1 |
    +---+---+---+---+---+---+---+
    C'est une supposition, essaie de comprendre ce qui l'a perdu et essaie d'écrire un code qui fait cela, ou essaie d'écrire toi même une fonction
    memcpy()
    et vois pourquoi la fin n'est pas correcte.

    Donc s'il n'y a pas recouvrement, les fonctions sont équivalentes (mais
    memcpy()
    est plus rapide), s'il y recouvrement la fonction
    memcpy()
    peut avoir un résultat faux.
    0
  4. leoliom Messages postés 187 Statut Membre 2
     
    D'après votre exemple:
    int tab[10] = {1, 2, 3, 4, 5, 6, 7};
    int tab2[10];
    strncpy(tab2,tab1,4);
    
    memmove(tab + 3, tab2, strlen(tab))
    memcpy(tab + 3, tab2, strlen(tab))


    Est-ce correct?

    J'ai pas compris la phrase C'est quand des cases sont à la fois dans la source à copier et dans la destination.

    Par exemple dans votre exemple il y a recouvrement pour quel nombre?
    0
    1. Dalfab Messages postés 638 Date d'inscription   Statut Membre Dernière intervention   102
       
      Tu n'as en effet pas compris. Il n'y a qu'un seul tableau et on fait des copies à l'intérieur du tableau. S'il y a deux tableaux alors
      memcpy()
      et
      memmove()
      sont indistinguables.
      Et
      strncpy()
      et
      strlen()
      sont des fonctions qui s'appliquent à des chaines de caractères. Ici ce sont des tableaux d'entiers, ça n'a rien à voir et ces fonctions font alors n'importe quoi dans ton code!
      Mon exemple c'est :
      int tab[7] = {1, 2, 3, 4, 5, 6, 7};
      memmove( &tab[3], &tab[0], 4*sizeof(int) );  // copier tab[0]tab[1]tab[2]tab[3] dans tab[3]tab[4]tab[5]tab[6].
      for ( int i = 0 ; i < 7 ; ++i )
          printf( "%d ", tab[i] );
      0
  5. Vous n’avez pas trouvé la réponse que vous recherchez ?

    Posez votre question
  6. leoliom Messages postés 187 Statut Membre 2
     
    J pense que c'est bon j'ai compris la subtilité entre les deux,ça m'a pris beaucoup de temps avant de comprendre
    Merci
    0
  7. leoliom Messages postés 187 Statut Membre 2
     
    Bonjour,

    Pour comprendre le fonctionnement de
    memcpy
    et
    memmove
    . J'ai regardé leur implémentation en C, mais bizarrement elle ne fait pas toujours la même chose. Pour
    memmove
    , ça marche, mais pas
    memcpy
    .

    Exemple :

    #include <stdio.h>
    #include <string.h>
    
    void * Memcpy(void* dst, const void* src, unsigned int cnt)
    {
        char *pszDest = (char *)dst;
        const char *pszSource =( const char*)src;
        if((pszDest!= NULL) && (pszSource!= NULL))
        {
            while(cnt) //till cnt
            {
                //Copy byte by byte
                *(pszDest++)= *(pszSource++);
                --cnt;
            }
        }
        return dst;
    }
    
    int main(void){
        char chaine[7] = "aabbcc";
        Memcpy(chaine + 2, chaine, 4);
        printf("%s\n", chaine);
        return 0;
    }
    
    • Memcpy
      renvoie
      aaaaaa
      ;
    • memcpy
      renvoie
      aaaabb
      .


    Si je prends une autre implémentation sur Internet :

    #include <string.h>
    #include <stdio.h>
    
    void myMemCpy(void *dest, void *src, size_t n){
      // Typecast src and dest addresses to (char *)
      char *csrc = (char *)src;
      char *cdest = (char *)dest;
      
      // Copy contents of src[] to dest[]
      for (int i = 0; i < n; i++)
        cdest[i] = csrc[i];
    }
    
    int main(void){
        char str1[7] = "aabbcc";
        myMemMove(str1 + 2, str1, 4);
        printf("New string: %s\n", str1);
        return 0;
    }
    
    • mymencpy
      renvoie
      aaaaaa
      ;
    • memcpy
      renvoie
      aaaabb
      .


    Je comprends plus, leur implémentation en C me paraissait correcte. Qu'est-ce qui ne va pas ? Qu'est-ce qui manque à ces fonctions pour qu'il fasse le même travail que
    memcpy
    ?
    0
  8. mamiemando Messages postés 33228 Date d'inscription   Statut Modérateur Dernière intervention   7 944
     
    Bonjour,

    Que fond
    memmove
    et
    memcpy
    ?

    De manière générale il suffit de regarder ce que racontent
    man memmove
    et
    man memcpy
    (à taper dans google ou dans un terminal si tu es sous Linux)
    • memcpy
      copie un bloc mémoire d'une adresse vers une autre adresse. Le bloc cible doit être préalloué en mémoire pour accueillir le bloc source, sinon tu as en général une erreur de segmentation. La recopie se fait typiquement octet par octet (et pour rappel un caractère ASCII (
      char
      ) fait un octet, c'est pourquoi cette fonction est très proche de
      strcpy
      et donc dans
      <string.h>
      ) : on lit l'octet source courant, on le recopie dans l'octet cible correspondant, et on répète l'opération jusqu'à avoir traité tout le bloc.
    • Officiellement
      memmove
      déplace un bloc mémoire d'une adresse vers une autre adresse. Le bloc cible doit être préalloué en mémoire pour accueillir le bloc source, sinon tu as en général une erreur de segmentation. Il n'y aucune garantie sur ce qui restera au niveau du bloc de départ mais la plupart des implémentations se contentent de le laisser tel quel afin d'améliorer la performance (et pour ces implémentation,
      memmove
      fait exactement la même chose que
      memcpy
      quand les blocs sources et cibles ne se chevauchent pas). Dans ces situations, on utilise plus
      memmove
      pour la lisibilité du programme. Cependant le bloc source ne doit plus être lu car selon l'implémentation, il pourrait avoir été réinitialisé.


    Que se passe-t'il si les blocs se chevauchent ?

    Maintenant, regardons un peu plus dans le détail ce qui se passe, si le bloc source et cible se chevauchent. Ici, il faut être prudent, car tu peux craindre des effets de bords (typiquement si tu écris dans la zone que tu t'apprêtes à lire):
    • D'après cette page,
      memmove
      passe par un buffer intermédiaire pour se prémunir de ces effets de bord .
    • D'après cette page, ce n'est pas le cas de
      memcpy
      , et donc il faut être vigilant à ce qu'on fait.


    Pourquoi existe-il une fonction
    strcpy
    et
    memcpy
    ?

    Il y a plusieurs raisons, et avant de commencer il est important de comprendre comment est stockée une chaîne de caractère en mémoire. Le problème de fond, c'est qu'une chaîne de caractère peut être arbitrairement longue (contrairement à un
    uint32_t
    qui fait par définition exactement 32 bits soit 4 octets). Comment garder trace de la taille ?

    Le choix qui a été fait, c'est de marquer la chaîne par un caractère terminal
    '\0'
    (qui correspond à l'
    uint8_t
    0
    . Toutes les fonctions
    str*
    (
    strcpy
    ,
    strlen
    , ...) et
    printf
    s'appuient sur ce caractère terminal pour savoir quand s'arrêter. Cela signifie que la chaîne
    "abc"
    fait 4 octets
    {'a', 'b', 'c', '\0'}
    et non 3 comme on pourrait le croire. Cela signifie qu'il ne faut pas oublier ce fameux caractère terminal dès qu'on manipule des chaînes. Sans lui, les fonctions
    str*
    jusqu'à rencontrer (par chance) un octet qui vaut 0 et qui souvent est en dehors d'une zone mémoire allouée, engendrant une erreur de segmentation.

    Si on revient à
    memcpy
    , on s'aperçoit qu'on peut reproduire
    strcpy
    sous réserve de connaître la longueur de la chaîne à recopier (sans oublier son
    '\0'
    terminal).

    Comme
    memcpy
    est conçue pour manipuler un bloc mémoire arbitraire -d'où, dans son prototype, le type
    const void *
    pour le bloc source (read only) et le type
    void *
    pour le bloc cible (read write)- (en particulier, quelque chose qui n'est pas une chaîne et donc quelque chose qui n'est pas nécessairement terminé par
    '\0'
    ), il faut lui indiquer combien d'octets recopier. C'est le rôle de son troisième paramètre.

    Retour à ton code

    Dans ton cas, vu que tu manipules des chaînes, il ne suffit pas de recopier les lettres pour que ça se passe bien, il faut s'assurer que la chaîne que tu vas copier ou que tu vas afficher possède bien son caractère terminal
    '\0'
    . C'est quelque chose que garantit par
    strcpy
    , mais pas par
    memcpy
    .

    Bonne chance
    0
  9. leoliom Messages postés 187 Statut Membre 2
     
    Ah donc les implémenterions de ces fonctions ne sont pas bonne car on prend pas en compte le caractère terminal de fin de chaine

    Esceque je dois modifier le code de sorte qu’il ajoute à la fin de la copie ce caractère pour que ça marche ?
    0
    1. Dalfab Messages postés 638 Date d'inscription   Statut Membre Dernière intervention   102
       
      >>> Ah donc les implémenterions de ces fonctions ne sont pas bonne car on prend pas en compte le caractère terminal de fin de chaine
      Non. As-tu vus quelque part que memcpy() devait mettre un terminateur de chaine?

      >>> Esceque je dois modifier le code de sorte qu’il ajoute à la fin de la copie ce caractère pour que ça marche ?
      Non.

      La fonction memcpy(), la fonction mymencpy() et la fonction Memcpy() sont toutes les 3 conformes.
      La norme dit :
      - s'il n'y a pas recouvrement, la fonction doit copier (tu peux vérifier que les 3 fonctions effectuent bien la copie).
      - s'il y a recouvrement, le résultat est totalement imprévisible (donc le résultat est tout à fait quelconque, ça n'a aucun sens de comparer les résultats dans ce cas!)

      C'est tout à fait possible que la fonction memcpy() soit différente d'elle même si tu changes le contexte!
          char str1[7] = "aabbcc";
          char str2[7] = "aabbcc";
          memcpy(str1 + 2, str1, 4);
          printf("New string: %s\n", str1);
          memcpy(str2 + 2, str2, 4);
          printf("New string: %s\n", str2);
      Ce code peut tout à fait retourner 2 résultats différents avec apparemment les mêmes données en entrée, ou avoir un résultat différent en version debug et en version release, essaie. C'est sans issue de vouloir reproduire ce que tu vois dans le cas où la fonction a un résultat indéfini!
      0
      1. mamiemando Messages postés 33228 Date d'inscription   Statut Modérateur Dernière intervention   7 944 > Dalfab Messages postés 638 Date d'inscription   Statut Membre Dernière intervention  
         
        Pour compléter ce que dit Dalfab :
        • les fonctions
          str*
          font l'hypothèse que le bloc mémoire à copier est de taille arbitraire mais peut-être vu comme une chaîne de caractère terminée par
          '\0'
          ;
        • les fonctions
          mem*
          ne font pas d'hypothèse sur un caractère d'arrêt (ce qui permet de recopier n'importe quel séquence d'octet, mais à besoin d'une taille pour savoir quand s'arrêter ;
        • dans tous les cas, ces fonctions ne doivent lire et écrire que dans des zones mémoires allouées par le programme, sans quoi une erreur de segmentation sera déclenchée.
        0
  10. leoliom Messages postés 187 Statut Membre 2
     
    Merci pour toutes ces réponses ,j’i mieux compris maintenant
    0