web-dev-qa-db-fra.com

Copie de la structure en C avec assignation au lieu de memcpy ()

Jusqu'à récemment, je n'ai vu que la copie des champs de structure effectuée avec memcpy(). Dans les cours et les instructions en ligne, la copie du contenu d'une structure dans une autre ressemble généralement à

struct block *b0 = malloc(sizeof(struct block));
struct block *b1 = malloc(sizeof(struct block));
/* populate fields in *b0 */
memcpy(b1, b0, sizeof *b1); /* copy contents of b0 into b1 */
/* free b0, b1 */

Cependant, cette tâche peut également être accomplie par une simple affectation remplaçant la memcpy().

*b1 = *b0; /* dereferenced struct assignment */

Y a-t-il une bonne raison pour laquelle ce n'est pas aussi largement utilisé (au moins dans mon expérience limitée)? Ces deux méthodes — affectation et memcpy()— sont-elles équivalentes, ou existe-t-il une raison impérieuse d'utiliser memcpy() en général?

22
olliezhu

Les deux méthodes sont équivalentes et effectuent une copie superficielle . Cela signifie que la structure elle-même est copiée, mais tout ce que la structure référence n'est pas copié.

Quant à savoir pourquoi memcpy est plus populaire, je ne suis pas sûr. Les anciennes versions de C ne prenaient pas en charge l'attribution de structure ( bien qu'il s'agisse d'une extension courante dès 1978 ), alors peut-être que le style memcpy est resté comme un moyen de rendre le code plus portable? Dans tous les cas, l'attribution de structure est largement prise en charge dans les compilateurs PC, et l'utilisation de memcpy est plus sujette aux erreurs (si vous obtenez une taille incorrecte, de mauvaises choses sont susceptibles de se produire), et il est donc préférable d'utiliser l'attribution de structure lorsque c'est possible.

Il existe cependant des cas où seul memcpy fonctionne. Par exemple:

  • Si vous copiez une structure vers ou depuis un tampon non aligné - par exemple, pour enregistrer/charger sur/depuis le disque ou envoyer/recevoir sur un réseau - vous devez utiliser memcpy, car l'attribution de structure nécessite à la fois la source et destination à aligner correctement.
  • Si vous compilez des informations supplémentaires après une structure, peut-être en utilisant un tableau à zéro élément , vous devez utiliser memcpy et factoriser ces informations supplémentaires dans le champ de taille.
  • Si vous copiez un tableau de structures, il peut être plus efficace pour faire un seul memcpy plutôt que de boucler et de copier les structures individuellement. Là encore, il se peut que non. Il est difficile de dire que les implémentations de memcpy diffèrent dans leurs caractéristiques de performances.
  • Certains compilateurs intégrés peuvent ne pas prendre en charge l'attribution de structure. Il y a probablement d'autres choses plus importantes que le compilateur en question ne prend pas également en charge, bien sûr.

Notez également que bien qu'en C memcpy et l'affectation de structure sont généralement équivalents, en C++ memcpy et l'affectation de structure sont pas équivalent. En général, le C++ est préférable d'éviter les structures memcpy, car l'affectation de structure peut, et est souvent, surchargée pour effectuer des tâches supplémentaires telles que des copies complètes ou la gestion du nombre de références.

31
bdonlan

Cela ne peut pas être la réponse exacte que vous recherchez.

Im expliquant le scénario que j'ai rencontré.

lorsque nous utilisons memcpy(), il effectue une copie octet par octet vers la destination. donc ne vous inquiétez pas de l'alignement des données dans l'architecture ARM. Si vous utilisez =, et n'importe laquelle des adresses n'est pas alignée sur 4 octets, un défaut d'alignement se produira.

Depuis le site Arm:

Un pointeur vers l'emplacement de destination qui est un octet au-delà du dernier octet écrit. Cela permet la poursuite du processus d'écriture with perfect alignment of bytes pour la concaténation de chaînes de blocs de mémoire.

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0175k/Cihbbjge.html

6
Jeyaram

Je ressuscite cette vieille question parce que les réponses n'expliquent pas pourquoimemcpy est en fait préféré.

memcpy est préféré car il indique clairement que le programmeur veut copier le contenu et pas simplement les pointeurs.

Dans l'exemple suivant, les deux affectations font deux choses très différentes:

struct Type *s1,*s2;
*s1=*s2;
s1=s2;

Par inadvertance, l'utilisation de l'un au lieu de l'autre peut avoir des effets désastreux. Le compilateur ne se plaindra pas. À moins que le programme ne se bloque lorsqu'un pointeur non initialisé est utilisé, l'erreur peut passer inaperçue pendant longtemps et produire des effets secondaires étranges.

L'écrire comme l'un des:

memcpy(s1,s2,sizeof(*s1));
memcpy(s1,s2,sizeof(*s2));
memcpy(s1,s2,sizeof(struct Type));

que le lecteur sache que l'intention est de copier le contenu (au détriment de la sécurité du type et de la vérification des limites).

Certains compilateurs (gcc par exemple) émettent même un avertissement sur la taille de lorsqu'ils rencontrent quelque chose comme:

memcpy(s1,s2,sizeof(s1));
4
Frédéric Marchal

Certaines personnes préfèrent memcpy parce que c'est ce qu'elles ont appris et elles n'ont jamais compris qu'elles pouvaient simplement faire une tâche (dans les temps anciens, la tâche n'était pas autorisée, mais c'est il y a très longtemps). Il n'y a aucun problème d'alignement à se soucier car la mémoire allouée par malloc () est toujours alignée correctement. Et puisqu'un compilateur pourrait traduire trivialement cette affectation en un appel memcpy, il ne serait jamais plus lent ou plus de code que memcpy. Bien sûr, il existe des systèmes embarqués avec des compilateurs très obsolètes.

0
gnasher729