Alias ​​ssortingct et inheritance de superposition

Considérons cet exemple de code:

#include  typedef struct AA; struct A { int x; int y; }; typedef struct BB; struct B { int x; int y; int z; }; int main() { B b = {1,2,3}; A *ap = (A*)&b; *ap = (A){100,200}; //a clear http://port70.net/~nsz/c/c11/n1570.html#6.5p7 violation ap->x = 10; ap->y = 20; //lvalues of types int and int at the right addrresses, ergo correct ? printf("%d %d %d\n", bx, by, bz); } 

J’avais l’habitude de penser que quelque chose comme fondre B * sur A * et utiliser A * pour manipuler l’object B * était une violation de pseudonyme ssortingcte. Mais ensuite, j’ai réalisé que la norme n’exigeait vraiment que:

Un object doit avoir sa valeur stockée accessible uniquement par une expression lvalue qui possède l’un des types suivants: 1) un type compatible avec le type effectif de l’object, (…)

et les expressions telles que ap->x ont le type et l’adresse corrects, et le type d’ ap ne devrait pas vraiment avoir d’importance ici (ou l’est-il?). Cela impliquerait, à mon sens, que ce type d’inheritance de superposition est correct tant que la sous-structure n’est pas manipulée dans son ensemble.

Cette interprétation est-elle erronée ou en contradiction apparente avec les intentions des auteurs de la norme?

La ligne avec *ap = est une violation de pseudonyme ssortingcte: un object de type B est écrit à l’aide d’une expression lvalue de type A

Supposons que cette ligne ne soit pas présente et que nous passions à ap->x = 10; ap->y = 20; ap->x = 10; ap->y = 20; . Dans ce cas, une lvalue de type int est utilisée pour écrire des objects de type int .

Il n’ya pas d’accord sur le sharepoint savoir s’il s’agit ou non d’une violation ssortingcte du créneau. Je pense que la lettre de la norme dit que ce n’est pas le cas, mais d’autres (y compris les développeurs gcc et clang) considèrent que ap->x implique que *ap été utilisé. La plupart s’accordent pour dire que la définition standard du pseudonyme ssortingct est trop vague et doit être améliorée.

Exemple de code utilisant vos définitions de structure:

 void f(A* ap, B* bp) { ap->x = 213; ++bp->x; ap->x = 213; ++bp->x; } int main() { B b = { 0 }; f( (A *)&b, &b ); printf("%d\n", bx); } 

Pour moi, les sorties 214 à -O2 et 2 à -O3 , avec gcc. L’assemblage généré sur godbolt pour gcc 6.3 était:

 f: movl (%rsi), %eax movl $213, (%rdi) addl $2, %eax movl %eax, (%rsi) ret 

ce qui montre que le compilateur a réorganisé la fonction pour:

 int temp = bp->x + 2; ap->x = 213; bp->x = temp; 

et par conséquent le compilateur doit considérer que ap->x ne peut pas alias bp->x .

Lors de la rédaction de C89, il aurait été peu pratique pour un compilateur de respecter les garanties de séquence initiale communes pour les syndicats sans les défendre également pour les pointeurs de liaison. En revanche, spécifier les garanties CIS pour les pointeurs de struct ne signifierait pas que les syndicats afficheraient un comportement similaire si leur adresse n’était pas prise. Étant donné que les garanties du SIC sont applicables aux pointeurs de struct depuis janvier 1974 – avant même l’ajout du mot-clé d’ union texte – et qu’une grande partie du code reposait depuis des années sur un tel comportement dans des circonstances qui ne pouvaient impliquer de manière plausible des objects d’ union type, et que les auteurs de la C89 étaient plus intéressés par la concision de la norme que par sa “preuve linguistique”, je dirais que la spécification par C89 de la règle de la CEI en termes de syndicats plutôt que de points de repère était presque certainement motivée par un désir d’éviter les licenciements, plutôt que par le désir de laisser aux compilateurs la liberté de faire tout leur possible pour violer plus de 15 années de précédent dans l’application des garanties CIS aux structureurs pointeurs.

Les auteurs de C99 ont reconnu que, dans certains cas, l’application de la règle CIS à la structure des pointeurs risquait de compromettre une optimisation utile, et ont précisé que, si un pointeur d’un type de structure est utilisé pour inspecter un CIS d’un membre d’un autre, la garantie CIS sera gagnante. Ne tient pas sauf si une définition d’un type d’union complet contenant les deux structures est dans la scope. Ainsi, pour que votre exemple soit compatible avec C99, il devrait contenir une définition d’un type d’ union contenant les deux structures. Cette règle semble avoir été motivée par le souhait de permettre aux compilateurs de limiter l’application du SIC aux cas où ils auraient des raisons de s’attendre à ce que deux types soient utilisés de manière connexe, et au code d’indiquer que les types sont liés sans avoir d’append une nouvelle construction de langage à cette fin.

Les auteurs de gcc semblent penser que, puisqu’il est inhabituel qu’un code reçoive un pointeur sur un membre d’un syndicat et souhaite ensuite accéder à un autre membre d’un syndicat, la simple visibilité d’une définition complète du type d’un syndicat ne devrait pas être suffisante. obliger un compilateur à respecter les garanties du SIC, même si la plupart des utilisations du SIC avaient toujours été centrées sur les indicateurs de structure plutôt que sur les syndicats. Par conséquent, les auteurs de gcc refusent de prendre en charge des constructions comme la vôtre, même dans les cas où la norme C99 l’exigerait.