Quand faut-il recourir aux syndicats ? Pourquoi en avons-nous besoin ?
Réponses
Trop de publicités?Les unions sont souvent utilisées pour convertir les représentations binaires des entiers et des flottants :
union
{
int i;
float f;
} u;
// Convert floating-point bits to integer:
u.f = 3.14159f;
printf("As integer: %08x\n", u.i);
Bien qu'il s'agisse d'un comportement techniquement indéfini selon la norme C (vous êtes censé ne lire que le champ qui a été écrit le plus récemment), il se comportera de manière bien définie dans pratiquement tous les compilateurs.
Les unions sont aussi parfois utilisées pour implémenter le pseudo-polymorphisme en C, en donnant à une structure une étiquette indiquant le type d'objet qu'elle contient, puis en unissant les types possibles :
enum Type { INTS, FLOATS, DOUBLE };
struct S
{
Type s_type;
union
{
int s_ints[2];
float s_floats[2];
double s_double;
};
};
void do_something(struct S *s)
{
switch(s->s_type)
{
case INTS: // do something with s->s_ints
break;
case FLOATS: // do something with s->s_floats
break;
case DOUBLE: // do something with s->s_double
break;
}
}
Cela permet à la taille de struct S
pour n'être que de 12 octets, au lieu de 28.
Les unions sont particulièrement utiles en programmation embarquée ou dans les situations où un accès direct au matériel/à la mémoire est nécessaire. Voici un exemple trivial :
typedef union
{
struct {
unsigned char byte1;
unsigned char byte2;
unsigned char byte3;
unsigned char byte4;
} bytes;
unsigned int dword;
} HW_Register;
HW_Register reg;
Ensuite, vous pouvez accéder au registre comme suit :
reg.dword = 0x12345678;
reg.bytes.byte3 = 4;
L'endiannité (ordre des octets) et l'architecture du processeur sont bien sûr importantes.
Une autre fonction utile est le modificateur de bits :
typedef union
{
struct {
unsigned char b1:1;
unsigned char b2:1;
unsigned char b3:1;
unsigned char b4:1;
unsigned char reserved:4;
} bits;
unsigned char byte;
} HW_RegisterB;
HW_RegisterB reg;
Avec ce code, vous pouvez accéder directement à un seul bit dans le registre/adresse mémoire :
x = reg.bits.b2;
La programmation de systèmes de bas niveau en est un exemple raisonnable.
IIRC, j'ai utilisé des unions pour décomposer les registres matériels en bits composants. Ainsi, vous pouvez accéder à un registre de 8 bits (comme c'était le cas à l'époque où je faisais ça ;-) en le décomposant en bits.
(J'ai oublié la syntaxe exacte mais...) Cette structure permettrait d'accéder à un registre de contrôle en tant que control_byte ou via les bits individuels. Il serait important de s'assurer que les bits correspondent aux bits de registre corrects pour un endivemnt donné.
typedef union {
unsigned char control_byte;
struct {
unsigned int nibble : 4;
unsigned int nmi : 1;
unsigned int enabled : 1;
unsigned int fired : 1;
unsigned int control : 1;
};
} ControlRegister;
Je l'ai vu dans quelques bibliothèques comme un remplacement de l'héritage orienté objet.
Par exemple
Connection
/ | \
Network USB VirtualConnection
Si vous voulez que la "classe" de connexion soit l'une ou l'autre des catégories ci-dessus, vous pourriez écrire quelque chose comme :
struct Connection
{
int type;
union
{
struct Network network;
struct USB usb;
struct Virtual virtual;
}
};
Exemple d'utilisation dans libinfinity : http://git.0x539.de/?p=infinote.git;a=blob;f=libinfinity/common/inf-session.c;h=3e887f0d63bd754c6b5ec232948027cbbf4d61fc;hb=HEAD#l74
Les unions permettent à des membres de données qui s'excluent mutuellement de partager la même mémoire. Ceci est très important lorsque la mémoire est plus rare, comme dans les systèmes embarqués.
Dans l'exemple suivant :
union {
int a;
int b;
int c;
} myUnion;
Cette union occupera l'espace d'un seul int, plutôt que 3 valeurs int distinctes. Si l'utilisateur définit la valeur de a et ensuite fixer la valeur de b il écraserait la valeur de a puisqu'ils partagent tous deux le même emplacement mémoire.
- Réponses précédentes
- Plus de réponses