Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Taille d'une structure.

12 réponses
Avatar
Etienne
Salut, comment peut on définir une structure afin qu'elle fasse pile
poil la taille attendue.

Par exemple

typedef struct
{
unsigned short start;
unsigned short end;
struct asmScanSprite *next_bloc;
struct asmScanSprite *next_line;
} asmScanSprite;


Sur mon linux 64 bit cette structure prend 24 octects alors que je
pensais qu'elle n'en ferai que 20 (2 + 2 + 8 + 8)

Etienne

10 réponses

1 2
Avatar
Jean-Marc Bourguet
Etienne writes:

Salut, comment peut on définir une structure afin qu'elle fasse pile poil
la taille attendue.

Par exemple

typedef struct
{
unsigned short start;
unsigned short end;
struct asmScanSprite *next_bloc;
struct asmScanSprite *next_line;
} asmScanSprite;


Sur mon linux 64 bit cette structure prend 24 octects alors que je pensais
qu'elle n'en ferai que 20 (2 + 2 + 8 + 8)



La regle systematique la plus simple pour qu'une structure prenne le
moins de place possible, est de mettre les membres dans l'ordre
decroissant de contraintes d'alignement (en pratique, celles-ci sont des
puissances de 2). Mais meme alors, la structure aura une taille
multiple de la plus grande contrainte d'alignement.

Dans ton cas, un pointeur a vraisemblablement une contrainte de 8, donc
ca ne changera rien. Mais tu pouvais faire pire avec

typedef struct
{
unsigned short start;
struct asmScanSprite *next_bloc;
unsigned short end;
struct asmScanSprite *next_line;
} asmScanSprite;

qui aurait eu une taille de 32.


Certains compilateurs ont des moyens (attributs, pragma, ...) pour
indiquer qu'il ne faut pas respecter les contraintes d'alignment, quite
a payer le cout en performance.

A+

--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
Olivier Miakinen
Le 25/06/2012 17:42, Etienne a écrit :
Salut, comment peut on définir une structure afin qu'elle fasse pile
poil la taille attendue.



Pour quoi faire ?

Cela dit, il y a quelques techniques qui marchent plus ou moins
selon le type de système ou le type de compilateur, mais à ma
connaissance il n'y a pas de méthode qui soit garantie de
fonctionner *toujours* et *partout*.

Par exemple

typedef struct
{
unsigned short start;
unsigned short end;
struct asmScanSprite *next_bloc;
struct asmScanSprite *next_line;
} asmScanSprite;


Sur mon linux 64 bit cette structure prend 24 octets alors que je
pensais qu'elle n'en ferai que 20 (2 + 2 + 8 + 8)



Sachant qu'un pointeur de taille 2^n octets ne sera jamais aussi
efficace que s'il est aligné sur une frontière de 2^n octets, il
est normal que tes pointeurs de taille 8 (= 2^3) ne soient pas
alignés sur une frontière de 4 < 8.

Sur ton système, la structure suivante a plus de chances de ne
prendre que 20 octets, mais ce n'est même pas sûr (imagine un
tableau de 2 structures identiques avec la même contrainte
d'alignement) :

typedef struct
{
struct asmScanSprite *next_bloc;
struct asmScanSprite *next_line;
unsigned short start;
unsigned short end;
} asmScanSprite;

En revanche, tu pourrais choisir de faire en sorte de forcer
(plus ou moins) la taille à 24 :

typedef struct
{
unsigned short start;
unsigned short end;
char reserved[4];
struct asmScanSprite *next_bloc;
struct asmScanSprite *next_line;
} asmScanSprite;

Mais bien sûr, le jour où tu passeras à une machine 128 bits,
avec des short sur 4 octets et des pointeurs sur 16 octets,
tous tes calculs s'écrouleront.

D'où ma question initiale, que je pose de nouveau :
« Pour quoi faire ? »
Avatar
espie
In article ,
Jean-Marc Bourguet wrote:
Etienne writes:

Salut, comment peut on définir une structure afin qu'elle fasse pile poil
la taille attendue.

Par exemple

typedef struct
{
unsigned short start;
unsigned short end;
struct asmScanSprite *next_bloc;
struct asmScanSprite *next_line;
} asmScanSprite;


Sur mon linux 64 bit cette structure prend 24 octects alors que je pensais
qu'elle n'en ferai que 20 (2 + 2 + 8 + 8)



La regle systematique la plus simple pour qu'une structure prenne le
moins de place possible, est de mettre les membres dans l'ordre
decroissant de contraintes d'alignement (en pratique, celles-ci sont des
puissances de 2). Mais meme alors, la structure aura une taille
multiple de la plus grande contrainte d'alignement.

Dans ton cas, un pointeur a vraisemblablement une contrainte de 8, donc
ca ne changera rien. Mais tu pouvais faire pire avec

typedef struct
{
unsigned short start;
struct asmScanSprite *next_bloc;
unsigned short end;
struct asmScanSprite *next_line;
} asmScanSprite;

qui aurait eu une taille de 32.


Certains compilateurs ont des moyens (attributs, pragma, ...) pour
indiquer qu'il ne faut pas respecter les contraintes d'alignment, quite
a payer le cout en performance.



*ATTENTION*, ce genre de pratique est abominablement non portable.
Sur certaines archi, c'est pas juste un probleme de performances, c'est
carrement des trucs qui ne marchent plus du tout.

Pour Etienne: il y a sur la plupart des machines une notion d'alignement.
La plupart des valeurs de base (entier, long, pointeur) sont prevus pour
fonctionner s'ils sont stockes en memoire a des adresses "alignees", e.g.,
qui sont multiples de 2, 4, 8... c'est tout betement lie aux acces
memoire, le fait est que sur toutes les machines modernes (qui ont moins
de 15 ans), les acces se font sous forme de 'mots' qui font plus d'un
octet.

Bref, les contraintes precises sont dependantes de l'architecture...

Sur certaines archi un peu pourries (intel), si on ne respecte pas ces
contraintes d'alignement, on perd en perf: une instruction genere plus
de travail pour le proc, qui doit recuperer non plus un mot, mais deux
mots memoire, et reconstituer (par exemple) les 64 bits du pointeur a
partir des deux mots.

Sur des archi plus pures (genre sparc), ne pas respecter ces contraintes
conduit direct a une erreur, ce qui est une excellente chose: ca evite
de cacher des problemes de perf, et surtout ca permet de detecter du
code tout pourri...


En C, dans les structures de donnees:
- les champs sont dans l'ordre de la declaration
- le premier champ est a l'adresse de la structure elle-meme
- il peut y avoir des octets de remplissage (padding) dans la structure
et a la fin de la structure pour respecter les contraintes d'alignement.

Sur du code portable, tu fais confiance a sizeof(), qui te donnera
le bon resultat.

Si tes structures prennent trop de place, il convient de reordonner les
champs pour minimiser les bits de padding.

Si tu veux un layout precis en memoire, il vaut mieux avoir d'excellentes
raisons a ca: ca a l'air d'etre le cas, ta structure ressemblant a de la
prog bas-niveau.

Mais tu ne peux *pas* exactement controler le layout avec du C standard.
Si c'est l'API d'un composant que tu veux utiliser, celui-ci est peut-etre
livre avec des fichiers d'entete qui font la magie non portable que tu
veux, ou alors sinon, il faudra ecrire des declarations non portables,
dependant du compilo ET de l'archi.

Dans la mesure du possible, c'est bien d'isoler ce genre de code dans un
coin... quand on porte du code d'une plateforme a l'autre, il n'y a pas
grand chose de plus enervant que les incompatibilites 'gratuites', tout
ce code ecrit de facon non portable alors que du code portable ferait aussi
bien l'affaire et serait plus simple a maintenir...


desole, j'ai rante.
Avatar
Pierre Maurette
Etienne :
Salut, comment peut on définir une structure afin qu'elle fasse pile poil la
taille attendue.

Par exemple

typedef struct
{
unsigned short start;
unsigned short end;
struct asmScanSprite *next_bloc;
struct asmScanSprite *next_line;
} asmScanSprite;


Sur mon linux 64 bit cette structure prend 24 octects alors que je pensais
qu'elle n'en ferai que 20 (2 + 2 + 8 + 8)



La notion de faire /pile poil la taille attendue/ est un peu floue,
plus sérieusement on ne sait pas exactement ce qui est important pour
vous. Et comme il n'y a pas de solution vraiment portable, il va
falloir certainement faire des concessions.
Déjà, la taille de 24 n'est pas étonnante avec gcc et un sizeof des
pointeurs de 8. Il traîne dans la doc un algo de prévision des paddings
dans les structures, à appliquer éventuellement récursivement, qui
fonctionne. Ici, même en bougeant vos /short/, vous aurez toujours un
multiple de 8. L'algo laisse penser qu'avec un short au début et
l'autre à la fin, ça donnerait 32, est-ce le cas ? La raison (une
mnémotechnique en tout cas) est que une fois placés les paddings entre
éléments visant à aligner correctement chacun d'eux, un padding de fin
est ajouté de façon à ce qu'un tableau de structure (sans padding
externe, tableau oblige) préserve l'alignement des éléments dans les
structures.
Dans le contexte très particulier de gcc, avec des short à 2 et des
zorglub* à 8, ce 24 est fiable. Mais celà ne vous suffit sans doute pas
?
Une approche crade, économe en taille mais qui peut être catastrophique
en performances, serait de contraindre un alignement plus petit, 1 ou
2. En Visual C ce serait encadrer le typedef par #pragma pack(push, 2)
et #pragma pack(pop), et obtenir le même résultat sous gcc, avec des
__attributes__ (__packed__) ou un truc comme ça.
Une approche peut-être plus élégante (mais je le rappelle pour résoudre
quel problème exactement ?), certainement sans gros dommages en
performances, mais des structures plus grosses (32 ici) serait de
gonfler les short. Lisez la doc de stdint.h, en allant jusqu'aux types
intptr_t et uintptr_t.

--
Pierre Maurette
Avatar
espie
In article ,
Pierre Maurette wrote:
Une approche crade, économe en taille mais qui peut être catastrophique
en performances, serait de contraindre un alignement plus petit, 1 ou
2. En Visual C ce serait encadrer le typedef par #pragma pack(push, 2)
et #pragma pack(pop), et obtenir le même résultat sous gcc, avec des
__attributes__ (__packed__) ou un truc comme ça.



Deuxieme fois que je vois passer ca.
NON, c'est pas juste catastrophique en performances, c'est surtout non
portable, au point de crasher sur la plupart des architectures.

Il y a surtout Intel qui permet les acces non alignes au prix d'une
performance moindre, mais sur la plupart des processeurs autres, un acces non
aligne conduit tout droit a une fault (et un SIGBUS sur un Unix).
Avatar
Pierre Maurette
Marc Espie :
In article ,
Pierre Maurette wrote:
Une approche crade, économe en taille mais qui peut être catastrophique
en performances, serait de contraindre un alignement plus petit, 1 ou
2. En Visual C ce serait encadrer le typedef par #pragma pack(push, 2)
et #pragma pack(pop), et obtenir le même résultat sous gcc, avec des
__attributes__ (__packed__) ou un truc comme ça.



Deuxieme fois que je vois passer ca.
NON, c'est pas juste catastrophique en performances, c'est surtout non
portable, au point de crasher sur la plupart des architectures.

Il y a surtout Intel qui permet les acces non alignes au prix d'une
performance moindre, mais sur la plupart des processeurs autres, un acces non
aligne conduit tout droit a une fault (et un SIGBUS sur un Unix).



Faut pas crier comme ça. Je n'ai jamais écrit que c'était portable, un
truc avec un #pragma et déjà un code complètement différent entre VC et
gcc. gcc Intel, il eut peut-être fallu préciser effectivement.
Pour ce qui est de /la plupart des architectures/, la question n'est
pas du ressort du C portable dès le départ. Alors un QELQSLLC, et basta
?

--
Pierre Maurette
Avatar
Marc
Marc Espie wrote:

[pragma pack]

Deuxieme fois que je vois passer ca.
NON, c'est pas juste catastrophique en performances, c'est surtout non
portable, au point de crasher sur la plupart des architectures.



Tu es sûr ? Je croyais que quand on accédait à un champ d'une structure
"compactée" sur une telle architecture, gcc générait explicitement le
code pour lire (de façon alignée) le début et la fin du champ et le
reconstruire.
Avatar
Damien Wyart
> Deuxieme fois que je vois passer ca.
> NON, c'est pas juste catastrophique en performances, c'est surtout
> non portable, au point de crasher sur la plupart des architectures.



* Marc in fr.comp.lang.c:
Tu es sûr ? Je croyais que quand on accédait à un champ d'une
structure "compactée" sur une telle architecture, gcc générait
explicitement le code pour lire (de façon alignée) le début et la fin
du champ et le reconstruire.



Plus de détails ici :
http://stackoverflow.com/questions/8568432/is-gccs-attribute-packed-pragma-pack-unsafe

Et une discussion plus technique sur certains aspects du problème là :
http://gcc.gnu.org/bugzilla/show_bug.cgi?idQ628

--
DW
Avatar
Marc
Damien Wyart wrote:

Plus de détails ici :
http://stackoverflow.com/questions/8568432/is-gccs-attribute-packed-pragma-pack-unsafe

Et une discussion plus technique sur certains aspects du problème là :
http://gcc.gnu.org/bugzilla/show_bug.cgi?idQ628



Ah, tiens, j'avais lu cette entrée de bugzilla à l'époque, puis oublié.
Merci pour les liens.
Avatar
Etienne
Le 25/06/2012 17:45, Jean-Marc Bourguet a écrit :
Etienne writes:

Dans ton cas, un pointeur a vraisemblablement une contrainte de 8, donc
ca ne changera rien. Mais tu pouvais faire pire avec

typedef struct
{
unsigned short start;
struct asmScanSprite *next_bloc;
unsigned short end;
struct asmScanSprite *next_line;
} asmScanSprite;

qui aurait eu une taille de 32.

Certains compilateurs ont des moyens (attributs, pragma, ...) pour
indiquer qu'il ne faut pas respecter les contraintes d'alignment, quite
a payer le cout en performance.



Hum Ok.
En fait, mon problème ne permet pas la modification de la structure.
En gros j'ai une fonction assembleur qui me retourne une liste chainée.

Ce que je voulais faire c'est pouvoir caster un pointeur sur la
structure afin de pouvoir manipuler les données contenu dans un element
de la liste chainée.

En gros j'ai un pointeur de type void * retourné par ma fonction
assembleur, en castant ce pointeur j'aurai voulu pouvoir accèder au
champ de ma structure plus facilement !!!

Enfin c'est pas grave.
Merci !
1 2