Pourquoi utiliser l’adresse du premier élément de struct plutôt que de se structurer?

Je viens juste de trouver une autre base de code au travail où les développeurs utilisent systématiquement l’adresse du premier élément des structures lors de la copie / comparaison / configuration, plutôt que de la structure elle-même. Voici un exemple simple.

Il y a d’abord un type de structure:

typedef struct { int a; int b; } foo_t; 

Ensuite, il y a une fonction qui fait une copie d’une telle structure:

 void bar(foo_t *inp) { foo_t l; ... memcpy(&l.a, &inp->a, sizeof(foo_t)); ... } 

Je n’écrirais pas moi-même un appel à memcpy de cette manière et j’ai commencé par soupçonner que les développeurs originaux ne comprenaient simplement pas tout à fait les pointeurs et les structures en C. Cependant, maintenant j’ai vu ceci dans deux bases de code pas de développeurs communs alors je commence à douter de moi.

Pourquoi voudrait-on utiliser ce style?

Personne ne devrait faire ça. Si vous réorganisez les membres de struct, vous êtes en difficulté.

Au lieu de:

 memcpy(&l.a, &inp->a, sizeof(foo_t)); 

vous pouvez le faire:

 memcpy(&l, inp, sizeof(foo_t)); 

Bien que cela puisse être dangereux et trompeur, les deux instructions font la même chose ici, car C garantit qu’il n’ya pas de remplissage avant le premier membre de la structure.

Mais le mieux est de copier les objects de la structure en utilisant un opérateur d’affectation simple:

 l = *inp; 

Pourquoi voudrait-on utiliser ce style?

Je suppose: l’ignorance ou la mauvaise discipline.

On ne le ferait pas Si vous avez déjà déplacé a élément dans la structure ou que vous avez inséré un ou des membres avant lui, vous introduiriez un bogue de destruction de mémoire.

Ce code est dangereux car la réorganisation des membres de la structure peut entraîner l’access de memcpy au-delà des limites de la structure si le membre a n’est plus le premier membre.

Cependant, il est concevable que les membres soient intentionnellement classés dans la structure et que le programmeur ne souhaite en copier qu’un sous-ensemble, en commençant par le membre a et en exécutant jusqu’à la fin de la structure. Si tel est le cas, le code peut être sécurisé avec la modification suivante:

  memcpy(&l.a, &inp->a, sizeof(foo_t) - offsetof(foo_t, a)); 

Maintenant, les membres de la structure peuvent être réorganisés dans n’importe quel ordre et ce memcpy ne sortira jamais des limites.

C’est une très mauvaise habitude. La structure peut avoir un autre membre ajouté, par exemple. C’est une habitude insouciante et je suis surpris de lire que quiconque le ferait.

D’autres l’ont déjà noté; celui qui m’insecte est ceci:

 struct Foo rgFoo [3]; struct Foo *pfoo = &rgFoo [0]; 

au lieu de

 struct Foo *pfoo = rgfoo; 

Pourquoi deref le tableau par index et ensuite reprendre l’adresse? C’est déjà l’adresse, la seule différence notable est que pfoo est techniquement

 struct Foo *const, 

ne pas

 struct Foo *. 

Pourtant, je voyais le premier tout le temps.

En fait, il existe un cas d’utilisation légitime: construire une hiérarchie de classes.

Lors du traitement des structures en tant qu’instances de classe, le premier membre (c’est-à-dire le décalage 0) sera généralement l’instance de supertype … si un sur-type existe. Cela permet à une dissortingbution simple de se déplacer entre l’utilisation du sous-type et le supertype. Très utile.

Sur la note de Darren Stone à propos de l’intention, cela est attendu lors de l’exécution d’OO en langage C.

Dans tous les autres cas , je suggère d’éviter ce schéma et d’accéder directement au membre pour les raisons déjà citées.