Différence entre memcpy et memmove

Résolu/Fermé
leoliom Messages postés 171 Date d'inscription jeudi 21 juillet 2016 Statut Membre Dernière intervention 20 février 2024 - 24 févr. 2022 à 11:15
leoliom Messages postés 171 Date d'inscription jeudi 21 juillet 2016 Statut Membre Dernière intervention 20 février 2024 - 7 mars 2022 à 17:31
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

jeannets Messages postés 26996 Date d'inscription dimanche 9 septembre 2007 Statut Contributeur Dernière intervention 15 juin 2024 5 799
24 févr. 2022 à 11:50
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
leoliom Messages postés 171 Date d'inscription jeudi 21 juillet 2016 Statut Membre Dernière intervention 20 février 2024 2
24 févr. 2022 à 12:18
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
Dalfab Messages postés 706 Date d'inscription dimanche 7 février 2016 Statut Membre Dernière intervention 2 novembre 2023 101
24 févr. 2022 à 14:41
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
leoliom Messages postés 171 Date d'inscription jeudi 21 juillet 2016 Statut Membre Dernière intervention 20 février 2024 2
Modifié le 2 mars 2022 à 15:00
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
Dalfab Messages postés 706 Date d'inscription dimanche 7 février 2016 Statut Membre Dernière intervention 2 novembre 2023 101
Modifié le 2 mars 2022 à 15:02
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

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
leoliom Messages postés 171 Date d'inscription jeudi 21 juillet 2016 Statut Membre Dernière intervention 20 février 2024 2
Modifié le 25 févr. 2022 à 17:03
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
leoliom Messages postés 171 Date d'inscription jeudi 21 juillet 2016 Statut Membre Dernière intervention 20 février 2024 2
Modifié le 2 mars 2022 à 15:11
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
mamiemando Messages postés 33149 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 13 juin 2024 7 758
2 mars 2022 à 15:40
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
leoliom Messages postés 171 Date d'inscription jeudi 21 juillet 2016 Statut Membre Dernière intervention 20 février 2024 2
6 mars 2022 à 10:58
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
Dalfab Messages postés 706 Date d'inscription dimanche 7 février 2016 Statut Membre Dernière intervention 2 novembre 2023 101
7 mars 2022 à 07:34
>>> 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
mamiemando Messages postés 33149 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 13 juin 2024 7 758 > Dalfab Messages postés 706 Date d'inscription dimanche 7 février 2016 Statut Membre Dernière intervention 2 novembre 2023
7 mars 2022 à 16:33
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
leoliom Messages postés 171 Date d'inscription jeudi 21 juillet 2016 Statut Membre Dernière intervention 20 février 2024 2
7 mars 2022 à 17:31
Merci pour toutes ces réponses ,j’i mieux compris maintenant
0