69 votes

Copie d'une structure vers une autre

Je sais que je peux copier la structure membre par membre, mais au lieu de cela, puis-je faire un memcpy sur les structures ?

Est-il conseillé de le faire ?

Dans ma structure, j'ai également une chaîne de caractères comme membre que je dois copier dans une autre structure ayant le même membre. Comment puis-je le faire ?

4 votes

Pourquoi parlez-vous de "fonctions membres" dans une question étiquetée "C" ?

2 votes

Pourriez-vous nous montrer la structure ou du moins un extrait de celle-ci ? Je suppose que la chaîne n'est pas un membre fonction seulement un membre, car nous parlons d'un simple C ici. La "chaîne" est-elle un tableau de chars, un char* alloué dynamiquement ou quoi ?

82voto

unwind Points 181987

La copie par affectation simple est la meilleure, car elle est plus courte, plus facile à lire et présente un niveau d'abstraction plus élevé. Au lieu de dire (au lecteur humain du code) "copier ces bits de ici à là", et de demander au lecteur de réfléchir à l'argument de la taille de la copie, vous faites simplement une simple affectation ("copier cette valeur de ici à là"). Il ne peut pas y avoir d'hésitation sur le fait que la taille soit correcte ou non.

De plus, si la structure est fortement paddée, l'affectation pourrait faire en sorte que le compilateur émette quelque chose de plus efficace, puisqu'il n'a pas à copier le padding (et qu'il sait où il se trouve), mais mempcy() ne le fait pas, donc il copiera toujours le nombre exact d'octets que vous lui demandez de copier.

Si votre chaîne est un tableau réel, c'est-à-dire :

struct {
  char string[32];
  size_t len;
} a, b;

strcpy(a.string, "hello");
a.len = strlen(a.string);

Dans ce cas, vous pouvez toujours utiliser l'affectation ordinaire :

b = a;

Pour obtenir une copie complète. Cependant, pour les données de longueur variable modélisées de la sorte, ce n'est pas la manière la plus efficace d'effectuer la copie puisque le tableau entier sera toujours copié.

Attention cependant, copier des structures qui contiennent des pointeurs vers de la mémoire allouée au tas peut être un peu dangereux, car en faisant cela vous êtes crénelage le pointeur, ce qui rend généralement ambigu le propriétaire du pointeur après l'opération de copie.

Dans ce cas, la seule solution consiste à effectuer une "copie profonde", qui doit être placée dans une fonction.

41voto

Simone Points 6673

Depuis C90, vous pouvez simplement utiliser :

dest_struct = source_struct;

tant que la chaîne est mémorisée dans un tableau :

struct xxx {
    char theString[100];
};

Sinon, si c'est un pointeur, vous devrez le copier à la main.

struct xxx {
    char* theString;
};

dest_struct = source_struct;
dest_struct.theString = malloc(strlen(source_struct.theString) + 1);
strcpy(dest_struct.theString, source_struct.theString);

1 votes

Que se passe-t-il avant le C90 ? Quelle serait la différence dans l'affectation des structures entre les compilateurs pré-C90 et post-C90 si la structure contient un tableau comme dans votre exemple ?

0 votes

@cesss : Avant C90/C89 il n'y avait pas de standard formel pour le C, donc cela dépendait du compilateur que vous utilisiez. Tous ne supportaient pas l'affectation de struct s. Puisque C90 exige que les compilateurs le supportent, vous pouvez être sûr que tout compilateur conforme à C90 (ou ultérieur) vous permettra de le faire.

21voto

paxdiablo Points 341644

Si les structures sont de types compatibles, oui, vous pouvez, avec quelque chose comme :

memcpy (dest_struct, source_struct, sizeof (*dest_struct));

La seule chose dont vous devez être conscient est qu'il s'agit d'un peu profond copie. En d'autres termes, si vous avez une char * pointant vers une chaîne de caractères spécifique, les deux pointeront vers la même chaîne.

Et si l'on modifie le contenu de l'un de ces champs de type chaîne (les données que l'utilisateur de l'application char * pointe vers, et non le char * elle-même) changera l'autre aussi.

Si vous voulez une copie facile sans avoir à faire manuellement chaque champ, mais avec le bonus supplémentaire de copies de chaînes non superficielles, utilisez strdup :

memcpy (dest_struct, source_struct, sizeof (*dest_struct));
dest_struct->strptr = strdup (source_struct->strptr);

Cette opération copiera l'intégralité du contenu de la structure, puis copiera en profondeur la chaîne de caractères, en donnant effectivement une chaîne de caractères distincte à chaque structure.

Et, si votre implémentation C n'a pas de strdup (il ne fait pas partie de la norme ISO), Obtenez-en un d'ici .

6voto

Scott M Points 513

En C, memcpy n'est que bêtement risqué. Tant que vous avez les trois paramètres exactement corrects, qu'aucun des membres de la structure n'est un pointeur (ou que vous avez explicitement l'intention de faire une copie superficielle) et qu'il n'y a pas de gros trous d'alignement dans la structure que memcpy va perdre du temps à parcourir en boucle (ou que les performances n'ont jamais d'importance), alors par tous les moyens, memcpy. Vous ne gagnez rien, si ce n'est un code plus difficile à lire, fragile aux changements futurs et qui doit être vérifié manuellement lors des revues de code (parce que le compilateur ne le peut pas), mais pourquoi pas.

En C++, nous passons au risque le plus ridicule. Vous pouvez avoir des membres de types qui ne sont pas memcpyables en toute sécurité, comme std::string, ce qui fera que votre struct de réception deviendra une arme dangereuse, corrompant aléatoirement la mémoire à chaque utilisation. Vous pouvez avoir des surprises impliquant des fonctions virtuelles lors de l'émulation de copies de tranches. L'optimiseur, qui peut faire des choses merveilleuses pour vous parce qu'il a la garantie d'une connaissance complète des types lorsqu'il compile =, ne peut rien faire pour votre appel memcpy.

En C++, il existe une règle empirique : si vous voyez memcpy ou memset, quelque chose ne va pas. Il y a de rares cas où cela n'est pas vrai, mais ils ne concernent pas les structs. Vous utilisez memcpy quand, et seulement quand, vous avez des raisons de le faire. aveuglément copie octets .

D'un autre côté, Assignment est simple à lire, vérifie l'exactitude au moment de la compilation, puis passe intelligemment à l'action. valeurs au moment de l'exécution. Il n'y a pas d'inconvénient.

0 votes

"En C++, il y a une règle empirique - si vous voyez memcpy ou memset, quelque chose ne va pas." Cela semble être une déclaration forte étant donné que std::memcpy est littéralement la seule façon légale de faire certaines opérations en C++, cf. youtu.be/_qzMpk-22cc?t=1814

5voto

Conrad Meyer Points 2063

Vous pouvez memcpy ou vous pouvez simplement les assigner comme n'importe quelle autre valeur.

struct {int a, b;} c, d;
c.a = c.b = 10;
d = c;

0 votes

Cela vaut-il aussi pour les cordes, comme indiqué dans la question ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X