Qu’est-ce qui me manque dans cet échantillon K & R?

Voici un extrait d’un exemple de code dans The C Programming Language de K & R (section 8.7):

typedef long Align; union header { struct { union header *ptr; unsigned size; } s; Align x; }; typedef union header Header; 

Et voici l’extrait explicatif:

Pour simplifier l’alignement, tous les blocs sont des multiples de la taille de l’en-tête et l’en-tête est correctement aligné. Ceci est réalisé par une union qui contient la structure d’en-tête souhaitée et une instance du type le plus ressortingctif, que nous avons arbitrairement faite longue.

Donc, si je comprends bien, l’idée est de faire en sorte qu’une instance d’en- header occupe un nombre d’octets multiple de sizeof(Align) . Le but est parfaitement logique pour moi.

Mais considérons le cas où long correspond à 64 bits, int 32 bits et un pointeur à 64 bits. Dans ce cas, header.s sera de 64 + 32 = 96 bits. Ainsi, sizeof(header) sera de 96 bits, ce qui n’est pas un multiple de 64 comme prévu.

Dans un tel cas, je suppose qu’il serait nécessaire de définir Align comme quelque chose d’autre (peut-être double ). Mais je ne suis pas tout à fait sûr de bien comprendre ce qui dicterait ce choix pour une architecture particulière. Est-ce juste le “plus grand” type? Et que se passe-t-il si le type le plus gros est long – ne pourrait-il pas ne pas fonctionner comme je l’ai décrit plus haut?

Merci

Le compilateur ajustera la taille de l’en- header afin que ptr , size et x puissent tous être récupérés efficacement à partir d’éléments d’un tableau d’en- header[] . Sur la plupart des architectures, la présence de x signifie que sizeof(header) sera augmenté jusqu’à un multiple de sizeof(long) .

La taille d’une union est au moins aussi grande que celle de son plus grand membre. Ainsi, même si sizeof(s) est 12 et sizeof(long) est 8, comme dans votre exemple, vous aurez toujours sizeof(header) >= 12 . De même, l’alignement d’une union est l’alignement le plus important de l’un de ses membres. La présence du membre long garantit donc que l’alignement de l’en- header correspond au moins à l’alignement du long , qui serait 8 dans ce cas.

Si vous comstackz ce code, vous constaterez presque certainement que sizeof(header) est 16 et non 12. La raison en est que si vous avez un tableau d’objects en- header , le compilateur doit s’assurer que l’alignement de l’un des membres est correct. , et les tableaux sont stockés de manière contiguë en mémoire. Pour y parvenir, la taille doit être un multiple de l’alignement. Pour ce faire, le compilateur ajoute 4 octets supplémentaires de remplissage à la fin d’un object d’en- header .

Cela fait longtemps que je n’ai pas travaillé avec des structures. Mais, je les ai utilisés pas mal. Le principe de base était de disposer les membres dans un ordre décroissant de taille.

Au moment où ceci a été écrit, long s’alignerait sur la plus grande limite d’adressage. Je crois que c’est toujours le cas, mais je n’ai pas suivi les problèmes liés au matériel depuis longtemps. Certains matériels ont été conçus de manière à ce que les envois de longue durée ne soient pas traités efficacement à moins que leur adresse ne corresponde à la taille de long.

Je crois que l’intention est que l’en-tête s’aligne sur la prochaine limite d’adressage efficace pour longtemps. Ceci dépend du matériel. Si les éléments longs sont efficacement adressables sur des limites de 32 bits, la structure s’alignera de manière appropriée. Si les longs ne sont adressables efficacement que sur des limites de 64 bits, la structure utilisera 128 bits pour s’aligner correctement.

Cela fait longtemps (beaucoup trop longtemps) que j’ai lu le livre de K & R, mais je crois que vous avez raison. Vous devez taper Aligner sur quelque chose d’au moins la taille du pointeur sur la plate-forme en question + sizeof unsigned.

Mon hypothèse est que lors de la rédaction de ce dernier (et de sa dernière mise à jour), le système d’exploitation 32 bits dominait et que votre scénario échappait aux rédacteurs et aux éditeurs. Bien sûr, ils sont beaucoup plus intelligents / sages et nous pourrions être mal placés.

Pour être correct, sizeof (Align) doit être supérieur ou égal à sizeof (s) . Mais le compilateur d’aujourd’hui n’a vraiment rien à faire et fait le travail pour vous.

 #include  #include  typedef long Align; typedef union header { struct { union header *ptr; /* 64 bits */ unsigned size; /* 32 bits */ } s; /* subtotal 96 */ Align x; /* 64 bits -> should be greater than 96 */ } Header; /* total 128 bits */ typedef struct hair { union header *ptr; /* 64 bits */ unsigned size; /* 32 bits */ } Hair; /* subtotal 96 */ int main(void) { Header u; Hair h; printf("char %ld bits, %ld bytes\n", sizeof(char)*CHAR_BIT,(sizeof(char)*CHAR_BIT)/8); printf("unsigned %ld bits, %ld bytes\n", sizeof(unsigned)*CHAR_BIT,(sizeof(unsigned)*CHAR_BIT)/8); printf("int %ld bits, %ld bytes\n", sizeof(int) * CHAR_BIT, (sizeof(int) * CHAR_BIT)/8); printf("long int %ld bits, %ld bytes\n", sizeof(long) * CHAR_BIT, (sizeof(long) * CHAR_BIT)/8); printf("long long int %ld bits, %ld bytes\n", sizeof(long long) * CHAR_BIT, (sizeof(long long) * CHAR_BIT)/8); printf("float %ld bits, %ld bytes\n", sizeof(float) * CHAR_BIT, (sizeof(float) * CHAR_BIT)/8); printf("double %ld bits, %ld bytes\n", sizeof(double) * CHAR_BIT, (sizeof(double) * CHAR_BIT)/8); printf("long double %ld bits, %ld bytes\n\n", sizeof(long double) * CHAR_BIT, (sizeof(long double) * CHAR_BIT)/8); printf("Header u %ld bits, %ld bytes\n", sizeof(u) * CHAR_BIT, (sizeof(u) * CHAR_BIT)/8); printf("Header *ptr %ld bits, %ld bytes\n", sizeof(usptr) * CHAR_BIT, (sizeof(usptr) * CHAR_BIT)/8); printf("Header size %ld bits, %ld bytes\n", sizeof(ussize) * CHAR_BIT, (sizeof(ussize) * CHAR_BIT)/8); printf("Header x %ld bits, %ld bytes\n\n", sizeof(ux) * CHAR_BIT, (sizeof(ux) * CHAR_BIT)/8); printf("Hair h %ld bits, %ld bytes\n", sizeof(h) * CHAR_BIT, (sizeof(h) * CHAR_BIT)/8); printf("Hair *ptr %ld bits, %ld bytes\n", sizeof(h.ptr) * CHAR_BIT, (sizeof(h.ptr) * CHAR_BIT)/8); printf("Hair size %ld bits, %ld bytes\n", sizeof(h.size) * CHAR_BIT, (sizeof(h.size) * CHAR_BIT)/8); return 0; } 

La sortie est:

 $ ./union-limits char 8 bits, 1 bytes unsigned 32 bits, 4 bytes int 32 bits, 4 bytes long int 64 bits, 8 bytes long long int 64 bits, 8 bytes float 32 bits, 4 bytes double 64 bits, 8 bytes long double 128 bits, 16 bytes Header u 128 bits, 16 bytes Header *ptr 64 bits, 8 bytes Header size 32 bits, 4 bytes Header x 64 bits, 8 bytes Hair h 128 bits, 16 bytes Hair *ptr 64 bits, 8 bytes Hair size 32 bits, 4 bytes 

Comme vous pouvez le constater, les cheveux ont la même taille que Header. Mais pour les compilateurs qui ne font pas le travail, le bon est d’utiliser long double comme Align.

Prends soin de toi, Beco.