Avec memcpy, la destination ne peut pas du tout chevaucher la source. Avec memmoveça peut. Cela signifie que cela memmovepourrait être très légèrement plus lent quememcpy , car il ne peut pas faire les mêmes hypothèses.
Par exemple, memcpypeut toujours copier les adresses de bas en haut. Si la destination chevauche après la source, cela signifie que certaines adresses seront écrasées avant d'être copiées. memmovedétecterait cela et copier 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.
lors de l'utilisation de memcpy, comment puis-je garantir que les adresses src et dest ne se chevauchent pas? Dois-je m'assurer personnellement que src et dest ne se chevauchent pas?
Alcott le
6
@Alcott, n'utilisez pas memcpy si vous ne savez pas qu'ils ne se chevauchent pas - utilisez plutôt memmove. Lorsqu'il n'y a pas de chevauchement, memmove et memcpy sont équivalents (bien que memcpy puisse être très, très, très légèrement plus rapide).
bdonlan le
Vous pouvez utiliser le mot-clé "restrict" si vous travaillez avec de longs tableaux et que vous souhaitez protéger votre processus de copie. Par exemple, si votre méthode prend comme paramètres des tableaux d'entrée et de sortie et que vous devez vérifier que l'utilisateur ne passe pas la même adresse en entrée et en sortie. Pour en savoir plus, cliquez ici stackoverflow.com/questions/776283/…
DanielHsH
10
@DanielHsH 'restrict' est une promesse que vous faites au compilateur; il n'est pas appliqué par le compilateur. Si vous mettez `` restreindre '' vos arguments et que, en fait, vous avez un chevauchement (ou plus généralement, accédez aux données restreintes à partir d'un pointeur dérivé de plusieurs endroits), le comportement du programme est indéfini, des bogues étranges se produiront, et le compilateur ne vous en avertira généralement pas.
bdonlan
@bdonlan Ce n'est pas seulement une promesse faite au compilateur, c'est une exigence pour votre appelant. C'est une exigence qui n'est pas appliquée mais si vous enfreignez une exigence, vous ne pouvez pas vous plaindre si vous obtenez des résultats inattendus. Le non-respect d'une exigence est un comportement indéfini, tout comme i = i++ + 1n'est pas défini; le compilateur ne vous interdit pas d'écrire exactement ce code mais le résultat de cette instruction peut être n'importe quoi et différents compilateurs ou processeurs afficheront ici des valeurs différentes.
Mecki
33
memmovepeut gérer la mémoire qui se chevauchent, memcpyne peut pas.
Considérer
char[] str ="foo-bar";
memcpy(&str[3],&str[4],4);//might blow up
De toute évidence, la source et la destination se chevauchent maintenant, nous remplaçons "-bar" par "bar". C'est un comportement indéfini en utilisant memcpysi la source et la destination se chevauchent, donc dans ce cas, nous en avons besoin memmove.
@ultraman: Parce qu'il PEUT avoir été implémenté en utilisant un assemblage de bas niveau qui nécessite que la mémoire ne se chevauche pas. Si tel est le cas, vous pouvez par exemple générer un signal ou une exception matérielle au processeur qui abandonne l'application. La documentation spécifie qu'elle ne gère pas la condition, mais la norme ne spécifie pas ce qui se passera lorsque ces conditions seront vialoées (c'est ce qu'on appelle un comportement indéfini). Un comportement non défini peut tout faire.
Martin York
avec gcc 4.8.2, même memcpy accepte également les pointeurs de source et de destination qui se chevauchent et fonctionne très bien.
GeekyJ
4
@jagsgediya Bien sûr que oui. Mais comme memcpy est documenté pour ne pas prendre en charge cela, vous ne devez pas vous fier à ce comportement spécifique à l'implémentation, c'est pourquoi memmove () existe. Cela peut être différent dans une autre version de gcc. Cela peut être différent si gcc insère le memcpy au lieu d'appeler memcpy () dans la glibc, cela peut être différent sur une version plus ancienne ou plus récente de la glibc et ainsi de suite.
nos
De la pratique, il semble que memcpy et memmove ont fait la même chose. Un tel comportement indéfini profond.
La fonction memcpy () copie n octets de la zone mémoire src vers la zone mémoire dest. Les zones de mémoire ne doivent pas se chevaucher. Utilisez memmove (3) si les zones de mémoire se chevauchent.
La principale différence entre memmove()et memcpy()est que dans memmove()un tampon - la mémoire temporaire - est utilisé, il n'y a donc aucun risque de chevauchement. D'un autre côté, memcpy()copie directement les données de l'emplacement pointé par la source vers l'emplacement pointé par la destination . ( http://www.cplusplus.com/reference/cstring/memcpy/ )
Considérez les exemples suivants:
#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);return0;}
Comme vous vous y attendiez, ceci imprimera:
stackoverflow
stackstacklow
stackstacklow
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);return0;}
Mais, il semble que la sortie que vous avez mentionnée soit inversée !!
kumar
1
Lorsque j'exécute le même programme, j'obtiens le résultat suivant: stackoverflow stackstackstw stackstackstw // signifie qu'il n'y a AUCUNE différence de sortie entre memcpy et memmove
kumar
4
"est que dans" memmove () ", un tampon - une mémoire temporaire - est utilisé;" Ce n'est pas vrai. il dit «comme si» donc il doit simplement se comporter ainsi, non pas qu'il doit en être ainsi. C'est en effet pertinent car la plupart des implémentations de memmove font juste un XOR-swap.
dhein
2
Je ne pense pas que l'implémentation de memmove()soit nécessaire pour utiliser un tampon. Il est parfaitement autorisé à se déplacer sur place (tant que chaque lecture se termine avant toute écriture à la même adresse).
Toby Speight
12
En supposant que vous deviez implémenter les deux, l'implémentation pourrait ressembler à ceci:
void memmove (void* dst,constvoid* src,size_t count ){if((uintptr_t)src <(uintptr_t)dst){// Copy from back to front}elseif((uintptr_t)dst <(uintptr_t)src){// Copy from front to back}}void mempy (void* dst,constvoid* 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. memmovecopie toujours de manière à ce qu'il soit toujours en sécurité si srcet se dstchevauchent, alors memcpyque ce n'est pas grave comme le dit la documentation lors de l'utilisation memcpy, les deux zones de mémoire ne doivent pas chevaucher.
Par exemple, si les memcpycopies «recto verso» et les blocs de mémoire sont alignés comme ceci
[---- src ----][---- dst ---]
la copie du premier octet de srcà dstdétruit déjà le contenu des derniers octets desrc avant qu'ils aient été copiés. Seule la copie «recto verso» donnera des résultats corrects.
Maintenant échangez srcet dst:
[---- dst ----][---- src ---]
Dans ce cas, il est prudent de copier uniquement «recto verso» car la copie «recto verso» détruirait src déjà près de son devant lors de la copie du premier octet.
Vous avez peut-être remarqué que l' memmoveimplémentation ci-dessus ne teste même pas si elles se chevauchent réellement, elle vérifie simplement leurs positions relatives, mais cela seul rendra la copie sûre. Comme il memcpyutilise généralement le moyen le plus rapide possible de copier de la mémoire sur n'importe quel système, il memmoveest généralement implémenté comme suit:
void memmove (void* dst,constvoid* src,size_t count ){if((uintptr_t)src <(uintptr_t)dst
&&(uintptr_t)src + count >(uintptr_t)dst
){// Copy from back to front}elseif((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, s'il memcpycopie toujours «recto verso» ou «recto verso», il memmovepeut également être utilisé memcpydans l'un des cas de chevauchement, mais memcpypeut 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 à être copié, donc même si vous avez testé commentmemcpy 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 lorsque vous décidez lequel appeler?
À moins que vous savez que vous srcet dstne se chevauchent pas, l' appel memmovecomme il sera toujours conduire à des résultats corrects et est généralement aussi vite que cela est possible dans le cas de copie dont vous avez besoin.
Si vous le savez avec certitude srcet que vous dstne vous chevauchez pas, appelez memcpycar peu importe celui que vous appelez pour le résultat, les deux fonctionneront correctement dans ce cas, mais memmovene seront jamais plus rapides memcpyet si vous êtes malchanceux, cela peut même être plus lent, vous ne pouvez donc gagner que l'appel memcpy.
simplement à partir de la norme ISO / CEI: 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é par s2 étaient d'abord copiés dans un tableau temporaire de n caractères qui ne se chevauchent pas 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 s1et s2de se chevaucher, alors que le memmove()fait.
Il existe deux façons évidentes d'implémenter mempcpy(void *dest, const void *src, size_t n)(en ignorant la valeur de retour):
for(char*p=src,*q=dest; n-->0;++p,++q)*q=*p;
char*p=src,*q=dest;while(n-->0)
q[n]=p[n];
Dans la première implémentation, la copie procède des adresses basse vers haute, et dans la seconde, de haute vers basse. Si la plage à copier se chevauche (comme c'est le cas lors du défilement d'un framebuffer, par exemple), alors un seul sens de fonctionnement est correct, et l'autre écrasera les emplacements qui seront ensuite lus.
Une memmove()implémentation, 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().
Le code utilisateur ne peut pas faire cela bien sûr, car même après la conversion srcet dstvers un type de pointeur concret, ils ne pointent pas (en général) vers 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 de comportement indéfini.
Notez que dans la vraie vie, les implémentations ont tendance à être beaucoup plus complexes, pour obtenir les performances maximales 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 le plus simplement possible.
memmove peut gérer le chevauchement des régions source et destination, contrairement à memcpy. Parmi les deux, memcpy est beaucoup plus efficace. Donc, mieux vaut utiliser memcpy si vous le pouvez.
Cette réponse dit "peut-être un peu plus vite" et fournit des données quantitatives indiquant seulement une légère différence. Cette réponse affirme que l'un d'eux est "beaucoup plus efficace". Combien plus efficace avez-vous trouvé le plus rapide? BTW: Je suppose que vous voulez dire memcpy()et non memcopy().
chux - Réintégrer Monica
Le commentaire est basé sur la conférence du Dr Jerry Cain. Je vous demanderais d'écouter sa conférence à 36h00, seulement 2-3 minutes suffiront. Et merci pour la capture. : D
Réponses:
Avec
memcpy
, la destination ne peut pas du tout chevaucher la source. Avecmemmove
ça peut. Cela signifie que celamemmove
pourrait être très légèrement plus lent quememcpy
, 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 chevauche après la source, cela signifie que certaines adresses seront écrasées avant d'être copiées.memmove
détecterait cela et copier 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.la source
i = i++ + 1
n'est pas défini; le compilateur ne vous interdit pas d'écrire exactement ce code mais le résultat de cette instruction peut être n'importe quoi et différents compilateurs ou processeurs afficheront ici des valeurs différentes.memmove
peut gérer la mémoire qui se chevauchent,memcpy
ne peut pas.Considérer
De toute évidence, la source et la destination se chevauchent maintenant, nous remplaçons "-bar" par "bar". C'est un comportement indéfini en utilisant
memcpy
si la source et la destination se chevauchent, donc dans ce cas, nous en avons besoinmemmove
.la source
Depuis la page de manuel memcpy .
la source
La principale différence entre
memmove()
etmemcpy()
est que dansmemmove()
un tampon - la mémoire temporaire - est utilisé, il n'y a donc aucun risque de chevauchement. D'un autre côté,memcpy()
copie directement les données de l'emplacement pointé par la source vers l'emplacement pointé par la destination . ( http://www.cplusplus.com/reference/cstring/memcpy/ )Considérez les exemples suivants:
Comme vous vous y attendiez, ceci imprimera:
Mais dans cet exemple, les résultats ne seront pas les mêmes:
Production:
C'est parce que "memcpy ()" fait ce qui suit:
la source
memmove()
soit nécessaire pour utiliser un tampon. Il est parfaitement autorisé à se déplacer sur place (tant que chaque lecture se termine avant toute écriture à la même adresse).En supposant que vous deviez implémenter les deux, l'implémentation pourrait ressembler à ceci:
Et cela devrait assez bien expliquer la différence.
memmove
copie toujours de manière à ce qu'il soit toujours en sécurité sisrc
et sedst
chevauchent, alorsmemcpy
que ce n'est pas grave comme le dit la documentation lors de l'utilisationmemcpy
, les deux zones de mémoire ne doivent pas chevaucher.Par exemple, si les
memcpy
copies «recto verso» et les blocs de mémoire sont alignés comme cecila copie du premier octet de
src
àdst
détruit déjà le contenu des derniers octets desrc
avant qu'ils aient été copiés. Seule la copie «recto verso» donnera des résultats corrects.Maintenant échangez
src
etdst
:Dans ce cas, il est prudent de copier uniquement «recto verso» car la copie «recto verso» détruirait
src
déjà près de son devant lors de la copie du premier octet.Vous avez peut-être remarqué que l'
memmove
implémentation ci-dessus ne teste même pas si elles se chevauchent réellement, elle vérifie simplement leurs positions relatives, mais cela seul rendra la copie sûre. Comme ilmemcpy
utilise généralement le moyen le plus rapide possible de copier de la mémoire sur n'importe quel système, ilmemmove
est généralement implémenté comme suit:Parfois, s'il
memcpy
copie toujours «recto verso» ou «recto verso», ilmemmove
peut également être utilisémemcpy
dans l'un des cas de chevauchement, maismemcpy
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 à être copié, donc même si vous avez testé commentmemcpy
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 lorsque vous décidez lequel appeler?
À moins que vous savez que vous
src
etdst
ne se chevauchent pas, l' appelmemmove
comme il sera toujours conduire à des résultats corrects et est généralement aussi vite que cela est possible dans le cas de copie dont vous avez besoin.Si vous le savez avec certitude
src
et que vousdst
ne vous chevauchez pas, appelezmemcpy
car peu importe celui que vous appelez pour le résultat, les deux fonctionneront correctement dans ce cas, maismemmove
ne seront jamais plus rapidesmemcpy
et si vous êtes malchanceux, cela peut même être plus lent, vous ne pouvez donc gagner que l'appelmemcpy
.la source
L'un gère les destinations qui se chevauchent, l'autre pas.
la source
simplement à partir de la norme ISO / CEI: 9899, il est bien décrit.
Et
Lequel j'utilise habituellement selon la question, dépend de la fonctionnalité dont j'ai besoin.
En texte brut,
memcpy()
ne permet pass1
ets2
de se chevaucher, alors que lememmove()
fait.la source
Il existe deux façons évidentes d'implémenter
mempcpy(void *dest, const void *src, size_t n)
(en ignorant la valeur de retour):Dans la première implémentation, la copie procède des adresses basse vers haute, et dans la seconde, de haute vers basse. Si la plage à copier se chevauche (comme c'est le cas lors du défilement d'un framebuffer, par exemple), alors un seul sens de fonctionnement est correct, et l'autre écrasera les emplacements qui seront ensuite lus.
Une
memmove()
implémentation, dans sa forme la plus simple, testeradest<src
(d'une manière dépendante de la plate-forme) et exécutera la direction appropriée dememcpy()
.Le code utilisateur ne peut pas faire cela bien sûr, car même après la conversion
src
etdst
vers un type de pointeur concret, ils ne pointent pas (en général) vers 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 de comportement indéfini.Notez que dans la vraie vie, les implémentations ont tendance à être beaucoup plus complexes, pour obtenir les performances maximales 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 le plus simplement possible.
la source
memmove peut gérer le chevauchement des régions source et destination, contrairement à memcpy. 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 Cain, (Stanford Intro Systems Lecture - 7) Heure: 36:00
la source
memcpy()
et nonmemcopy()
.