Effectivement, un union résout tout :!:
Version imprimable
Sauf que la norme ne garantie pas que ce qui est mis d'un coté est lisible de l'autre... Notamment, rien ne dit que ce tu appelles bit0 dans le champ de bit corresponde au bit 0 de l'unsigned char. Si tu fais ça sur un 68k PowerPC, ça va te faire drôle... Et je ne parle pas d'un DSP où les unsigned char font 16 ou 32--bit...
Disons que ça peut résoudre ton problème particulier sur une plateforme bien définie...
Sur ma machine, c'est OK (Intel x86)
Mais je n'ai toujours pas compris en quoi c'était plus simple ou plus rapide que les opérateurs bits (qui eux, sont portables)...Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 #include <stdio.h> typedef union { struct { unsigned char bit0:1; unsigned char bit1:1; unsigned char bit2:1; unsigned char bit3:1; unsigned char bit4:1; unsigned char bit5:1; unsigned char bit6:1; unsigned char bit7:1; } field; unsigned char car; } Pixel; int main (void) { Pixel p; p.car = 0x01; /* BIT0 = 1 */ printf ("p.field.bit0 = %d\n", p.field.bit0); return 0; }
Pourtant SDL utilise l'union sans aucun problème et est portable (sur plus que Windows/Mac/Linux).
Par contre le bit0 du char ne correspondra pas forcément au bit0 de field (mais cela pose-t-il un problème?)
Concernant la concaténation (le problème du PO), pourquoi vouloir tout mettre dans un int, un tableau de deux char/Pixels prend la même place.
Et l'union permet de comparer deux pixels, si c'est que tu veux:
On aurait pu faire la même chose avec un cast barbare, mais c'est moins propre.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 #include <string.h> #include <stdio.h> typedef union { struct { char bit0:1; char bit1:1; char bit2:1; char bit3:1; char bit4:1; char bit5:1; char bit6:1; char bit7:1; } field; char ch; } Pixel; int main() { Pixel p[3]; /* Initiliasation à partir des valeurs en bit */ p[0].field.bit0 = 1, p[0].field.bit1 = 0, p[0].field.bit2 = 1, p[0].field.bit3 = 1, p[0].field.bit4 = 1, p[0].field.bit5 = 1, p[0].field.bit6 = 1, p[0].field.bit7 = 1; p[1].field.bit0 = 1, p[1].field.bit1 = 1, p[1].field.bit2 = 1, p[1].field.bit3 = 1, p[1].field.bit4 = 1, p[1].field.bit5 = 1, p[1].field.bit6 = 1, p[1].field.bit7 = 1; p[2].field.bit0 = 1, p[2].field.bit1 = 0, p[2].field.bit2 = 1, p[2].field.bit3 = 1, p[2].field.bit4 = 1, p[2].field.bit5 = 1, p[2].field.bit6 = 1, p[2].field.bit7 = 1; if (p[0].ch == p[1].ch) { puts("p[0] == p[1]"); } if (p[0].ch == p[2].ch) { puts("p[0] == p[2]"); } return 0; }
Un champ de bit qui utilise le type char n'est déjà pas portable. Ensuite, lorsqu'on utilise une union, on ne peut accéder à la valeur de cette dernière que par le champ ayant servi à son initialisation. Ainsi, si l'initialisation se fait par le champ ch, on ne peut pas accéder à field. La norme prédit un comportement indéterminé dans ce cas.
De la même manière qu'on ne peut pas faire:
ThierryCode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 #include <stdio.h> union monUnion { float f; long n; }; int main(void) { union monUnion a; a.f = 3.1415; /* -tc- Attention: comportement indetermine */ printf("%ld\n", a.n); return 0; }
J'ai vu ceci dans les headers de la SDL:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125 /* Application visibility event structure */ typedef struct SDL_ActiveEvent { Uint8 type; /* SDL_ACTIVEEVENT */ Uint8 gain; /* Whether given states were gained or lost (1/0) */ Uint8 state; /* A mask of the focus states */ } SDL_ActiveEvent; /* Keyboard event structure */ typedef struct SDL_KeyboardEvent { Uint8 type; /* SDL_KEYDOWN or SDL_KEYUP */ Uint8 which; /* The keyboard device index */ Uint8 state; /* SDL_PRESSED or SDL_RELEASED */ SDL_keysym keysym; } SDL_KeyboardEvent; /* Mouse motion event structure */ typedef struct SDL_MouseMotionEvent { Uint8 type; /* SDL_MOUSEMOTION */ Uint8 which; /* The mouse device index */ Uint8 state; /* The current button state */ Uint16 x, y; /* The X/Y coordinates of the mouse */ Sint16 xrel; /* The relative motion in the X direction */ Sint16 yrel; /* The relative motion in the Y direction */ } SDL_MouseMotionEvent; /* Mouse button event structure */ typedef struct SDL_MouseButtonEvent { Uint8 type; /* SDL_MOUSEBUTTONDOWN or SDL_MOUSEBUTTONUP */ Uint8 which; /* The mouse device index */ Uint8 button; /* The mouse button index */ Uint8 state; /* SDL_PRESSED or SDL_RELEASED */ Uint16 x, y; /* The X/Y coordinates of the mouse at press time */ } SDL_MouseButtonEvent; /* Joystick axis motion event structure */ typedef struct SDL_JoyAxisEvent { Uint8 type; /* SDL_JOYAXISMOTION */ Uint8 which; /* The joystick device index */ Uint8 axis; /* The joystick axis index */ Sint16 value; /* The axis value (range: -32768 to 32767) */ } SDL_JoyAxisEvent; /* Joystick trackball motion event structure */ typedef struct SDL_JoyBallEvent { Uint8 type; /* SDL_JOYBALLMOTION */ Uint8 which; /* The joystick device index */ Uint8 ball; /* The joystick trackball index */ Sint16 xrel; /* The relative motion in the X direction */ Sint16 yrel; /* The relative motion in the Y direction */ } SDL_JoyBallEvent; /* Joystick hat position change event structure */ typedef struct SDL_JoyHatEvent { Uint8 type; /* SDL_JOYHATMOTION */ Uint8 which; /* The joystick device index */ Uint8 hat; /* The joystick hat index */ Uint8 value; /* The hat position value: SDL_HAT_LEFTUP SDL_HAT_UP SDL_HAT_RIGHTUP SDL_HAT_LEFT SDL_HAT_CENTERED SDL_HAT_RIGHT SDL_HAT_LEFTDOWN SDL_HAT_DOWN SDL_HAT_RIGHTDOWN Note that zero means the POV is centered. */ } SDL_JoyHatEvent; /* Joystick button event structure */ typedef struct SDL_JoyButtonEvent { Uint8 type; /* SDL_JOYBUTTONDOWN or SDL_JOYBUTTONUP */ Uint8 which; /* The joystick device index */ Uint8 button; /* The joystick button index */ Uint8 state; /* SDL_PRESSED or SDL_RELEASED */ } SDL_JoyButtonEvent; /* The "window resized" event When you get this event, you are responsible for setting a new video mode with the new width and height. */ typedef struct SDL_ResizeEvent { Uint8 type; /* SDL_VIDEORESIZE */ int w; /* New width */ int h; /* New height */ } SDL_ResizeEvent; /* The "screen redraw" event */ typedef struct SDL_ExposeEvent { Uint8 type; /* SDL_VIDEOEXPOSE */ } SDL_ExposeEvent; /* The "quit requested" event */ typedef struct SDL_QuitEvent { Uint8 type; /* SDL_QUIT */ } SDL_QuitEvent; /* A user-defined event type */ typedef struct SDL_UserEvent { Uint8 type; /* SDL_USEREVENT through SDL_NUMEVENTS-1 */ int code; /* User defined event code */ void *data1; /* User defined data pointer */ void *data2; /* User defined data pointer */ } SDL_UserEvent; /* If you want to use this event, you should include SDL_syswm.h */ struct SDL_SysWMmsg; typedef struct SDL_SysWMmsg SDL_SysWMmsg; typedef struct SDL_SysWMEvent { Uint8 type; SDL_SysWMmsg *msg; } SDL_SysWMEvent; /* General event structure */ typedef union SDL_Event { Uint8 type; SDL_ActiveEvent active; SDL_KeyboardEvent key; SDL_MouseMotionEvent motion; SDL_MouseButtonEvent button; SDL_JoyAxisEvent jaxis; SDL_JoyBallEvent jball; SDL_JoyHatEvent jhat; SDL_JoyButtonEvent jbutton; SDL_ResizeEvent resize; SDL_ExposeEvent expose; SDL_QuitEvent quit; SDL_UserEvent user; SDL_SysWMEvent syswm; } SDL_Event;
Donc l'union commence par un Uint8:
Code:
1
2
3
4 typedef union SDL_Event { Uint8 type; //... } SDL_Event;
Et chaque Event différent aussi:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13 /* Application visibility event structure */ typedef struct SDL_ActiveEvent { Uint8 type; /* SDL_ACTIVEEVENT */ //... } SDL_ActiveEvent; /* Keyboard event structure */ typedef struct SDL_KeyboardEvent { Uint8 type; /* SDL_KEYDOWN or SDL_KEYUP */ //... } SDL_KeyboardEvent; //...
Et pour savoir le type d'un évènement, quoique contienne l'union, on fait:
Et pourtant, même si l'union contient un évènement SDL_ActiveEvent par exemple, on peut utiliser event.type, alors que ce n'est pas le "bon" champ.Code:
1
2
3
4
5
6
7
8 SDL_Event event; //... switch (event.type) { ... }
C'est bien sûr à cause du fait que chaque structure aie pour premier membre un Uint8, et donc en faisant event.type, à chaque fois on accède aux premiers 8 bits de l'union SDL_Event, ce qui signifie soit la variable "type" de l'union, soit la variable "type" de la structure correspondante.
Donc dans le cas de Pixel/char, on accède à chaque fois aux 8 premiers bits de l'union, mais c'est cette fois un comportement indéterminé?
Pour moi c'est la même chose :?
Si la plateforme supporte la syntaxe, OK. Mais c'est pas gagné d'avance. Il faut lire la doc, tester...
En tout cas, si ça marche aussi sur les anciens Mac (68k, PowerPC), il y a certainement une compilation conditionnelle pour inverser les bits
Ben oui. Si tu fais pixel.field.bit0=1 et que ça donne pixel.ch==1 (0x01) ou pixel.ch==128 (0x80) en fonction de la plateforme, c'est ce que j'appelle un problème...Citation:
Par contre le bit0 du char ne correspondra pas forcément au bit0 de field (mais cela pose-t-il un problème?)
Dans le cas où les deux champs possèdent une séquence commune, comme c'est le cas dans l'union SDL_Event que tu présentes:
Code:
1
2
3
4
5
6
7
8
9
10 /* General event structure */ typedef union SDL_Event { Uint8 type; struct SDL_ActiveEvent { Uint8 type; /*...*/ } SDL_ActiveEvent active; /*...*/ } SDL_Event;
Il est autorisé d'accéder à la valeur du champ active.type directement via le champ type. Ce cas particulier est prévu par la norme.
ThierryCitation:
Envoyé par ISO/IEC 9899:1999, &6.5.2.3
La norme l'autorise si le premier élément des unions de structures est de même type. Si c'est le cas ici, pas de problème.
La norme dit que le comportement est indéterminé si le type est différent.
Peu importe ce que c'est 'pour toi', la norme est claire, le comportement est indéterminé. Point.
J'ai signalé, dès que j'ai parlé d'une union (post #3 et #11), que ce ne serait pas portable. Toutefois le PO semble ne pas avoir le souci de la portabilité de son code pour son application, alors ....
Emmanuel
Moi non plus; Mais c'est l'idée du PO. Ils les a peut être codé d'une façon pas très optimale ? Mystère.Citation:
Mais je n'ai toujours pas compris en quoi c'était plus simple ou plus rapide que les opérateurs bits (qui eux, sont portables)...
je dois avouez que c'est pas plus simple ou plus rapide et que j'ai pas une préférence pour cela cependant les gens vous semblez oubliez un truc , je suis pas seul dessus et je suis pas non plus libre de tout faire car je doit etre certains que sa s'integre et que sa marche nickel avec le reste du prog.Citation:
Mais je n'ai toujours pas compris en quoi c'était plus simple ou plus rapide que les opérateurs bits (qui eux, sont portables)...
Le reste de l'application embarqué doit obligatoirement avoir acces simplement et sans se prendre le choux a quelques valeur du tableau de pixel.
Donc si tu dit a des personnes qui bosse après toi et qui on pas la même abilitation: tenez vous faites C.Bit0 ....
le produit sera opérationnel plus vite et sans doute sans avoir a débug en mass les codes qu'ils auront pondu derriere.
Je pense que la vous pensez que je suis en solo et que je fais sa pour le fun.... mais bien sur.
Déjà le code est confidentiel je en peu rien en dire et j'ai même pas le droit de le garder sur mon PC quand je rentre chez moi donc foutre un morceau ici je pense pas que sa soit possible .... j'ai pas envie d'etre viré ^^.
Sinon grande nouvelle j'ai fini par trouver une autre solution qui mixe des tableau de char et d'autre petit truc voila bravo pour ce débat ultra constructif:!!!!
Rien ne t'empêche à priori de créer une couche d'abstraction qui gère tes opérations bit à bit de manière portable et qui soit simple à utiliser pour ceux qui travaillent avec toi sur ce projet.
Thierry