Vous vous apprêtez à lire un tutoriel rédigé par un membre de ce site. Malgré tout le soin que ce membre a pu apporter au tutoriel, nous ne pouvons pas garantir que les informations contenues sur cette page sont exactes à 100%. Merci de garder cela en tête lorsque vous lirez cette page ;o)
Déclaration
Une union se déclare de la même façon qu'une structure, comme ceci :
Code : C1
2
3
4
5 | union MonUnion
{
int entier;
double reel;
};
|
Le mieux est de toujours déclarer une union dans un header avec tous vos prototypes.
Et maintenant voici comment déclarer une variable de type union dans une fonction :
Code : C1
2
3
4
5 | int main(void)
{
union MonUnion variable;
return 0;
}
|
Mais c'est la même chose qu'une structure ton union ?!

Non ! Maintenant que nous sommes prêts à utiliser notre union, vous allez voir toute la différence.
En fait, quand vous voulez utiliser une union, celle-ci ne peut utiliser que l'un des types que vous lui avez donné à la fois.
Je ne te suis pas là !?

Ce n'est pas grave, vous allez voir c'est très simple.
Utilisation
Par exemple (en gardant mon union de tout à l'heure) :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13 | int main(void)
{
union MonUnion variable;
variable.entier = 200;
printf("Valeur entière = %d\n",variable.entier);
variable.reel = 1200.05;
printf("Valeur flottante = %lf\n",variable.reel);
printf("Valeur entière = %d\n",variable.entier);
return 0;
}
|
Vous obtenez ceci :
Code : Console | Valeur entière = 200
Valeur flottante = 1200.050000
Valeur entière = 858993459 |
Comme vous le voyez, après avoir utilisé la partie réelle de mon union, la valeur de la partie entière a été modifiée.
Comment ça se fait ?

C'est très simple, en fait tout ce qui se trouve dans votre union partage la même zone de mémoire. Voici comment vérifier ceci :
Code : C 1
2
3
4
5
6
7
8
9
10 | int main(void)
{
union MonUnion variable;
printf("Adresse de l'union = %p\n",&variable);
printf("Adresse de la partie entière = %p\n",&variable.entier);
printf("Adresse de la partie réelle = %p\n",&variable.reel);
return 0;
}
|
Code : Console | Adresse de l'union = 0023FF70
Adresse de la partie entière = 0023FF70
Adresse de la partie réelle = 0023FF70 |
Donc pour vous expliquer clairement, tout à l'heure quand nous avions utilisé la partie entière, en partant du principe que la taille en octets d'un int est de 4, ces emplacements de mémoire ont été utilisés :
0023FF70
0023FF71
0023FF72
0023FF73
Mais en utilisant la partie entière, en partant du principe que la taille en octets d'un double est de 8, nous avons utilisé ces emplacements :
0023FF70
0023FF71
0023FF72
0023FF73
0023FF74
0023FF75
0023FF76
0023FF77
Nous avons donc écrasé ce qui se trouvait à l'intérieur de notre partie entière.
Mais c'est nul alors ton truc.
Mais non.

Ça peut être très utile, tout d'abord vous devez savoir que contrairement à une structure, une union ne prend en mémoire que la place utilisée par son type le plus grand, ainsi notre union de tout à l'heure ne prend que la taille d'un double en mémoire alors qu'une structure aurait fait la taille d'un int en plus de celle d'un double.
Démonstration
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | union MonUnion
{
int entier;
double reel;
};
struct MaStruct
{
int entier;
double reel;
};
int main(void)
{
union MonUnion variable;
struct MaStruct variable2;
printf("Taille de l'union = %d\n",sizeof(variable));
printf("Taille de la structure = %d\n",sizeof(variable2));
return 0;
}
|
Code : Console | Taille de l'union = 8
Taille de la structure = 16 |
Les unions peuvent s'avérer très utile dans certains cas, mais il faut faire
très attention à leur utilisation.
Par la suite nous verrons une utilisation un peu plus avancée d'une union combinée avec une structure.
Dans ce chapitre nous allons utiliser une union pour parser une chaîne tout comme le ferait la fonction printf par exemple mais pour une toute autre utilisation, nous allons créer une fonction qui additionne tous les arguments.
Création de l'union
Nous allons donc devoir créer une union pouvant supporter différents types voulus, comme ceci :
Code : C1
2
3
4
5
6
7
8
9 | union MyNum
{
char c;
short i;
long l;
float f;
double d;
char* s;
};
|
Afin d'utiliser les fonctions permettant d'utiliser les arguments variables, vous devez penser à inclure stdarg.h
Notre fonction prendra donc une chaîne de caractères en argument, et renverra un double (car le résultat peut être très grand).
Le prototype de notre fonction sera donc comme ceci :
Code : C1 | double my_num_somme(const char* format,...);
|
Voici donc la fonction :
Code : C 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 | double my_num_somme(const char* format,...)
{
double total = 0.0;
union MyNum num;
va_list ap;
va_start (ap,format);
while(*format != '\0')
{
switch(*format++)
{
/*l'argument est un char*/
case 'c':
num.c = (char)va_arg(ap,int);
total += num.c;
break;
/*l'argument est un short*/
case 'i':
case 'I':
num.i = (short)va_arg(ap,int);
total += num.i;
break;
/*l'argument est un long*/
case 'l':
case 'L':
num.l = va_arg(ap,long);
total += num.l;
break;
/*l'argument est un float*/
case 'f':
num.f = (float)va_arg(ap,double);
total += num.f;
break;
/*l'argument est un double*/
case 'F':
num.d = va_arg(ap,double);
total += num.d;
break;
/*conversion d'une chaîne représentant un entier*/
case 's':
num.s = va_arg(ap,char*);
total += atol(num.s);
break;
/*conversion d'une chaîne représentant un flottant*/
case 'S':
num.s = va_arg(ap,char*);
total += atof(num.s);
break;
default:
break;
}
}
va_end(ap);
return total;
}
|
Mais pourquoi tu passes un
int à va_arg pour le type
char et
short et un
double pour les
float ?

C'est tout simplement parce que le compiler, afin de se simplifier la vie, passe les entiers de type plus petits que des
int en tant que
int et les flottants de type
float en tant que
double.
L'avantage ici est donc que même si l'utilisateur envoie un nombre plus grand qu'un
int par exemple et qu'il donne à son format le caractère
c qui représente donc un
char sera tronqué, vous contrôlez donc bien le type selon le format.
Et dans tout ça, elle a servi à quoi l'union ?
À gagner de la place en mémoire et surtout de la clarté dans le code, car sans cette union vous auriez dû déclarer tous les types de variables différents vous-même.
Évidemment ici il s'agit d'un petit exemple, mais imaginez dans une fonction qui fait une centaine de lignes.
Voici un petit exemple de ce que donne notre fonction :
Code : C1
2
3
4
5 | int main(void)
{
printf("Total : %lf\n",my_num_somme("ifsS",100,5.8945,"10000","102.501"));
return 0;
}
|
Code : Console | Total : 10208.395500
Press ENTER to continue. |
Dans cette partie nous allons créer des variables intelligentes qui peuvent prendre plusieurs types différents et dont le type peut être connu.
Création de la variable
Nous allons donc tout d'abord créer une union qui pourra contenir les différents types voulus :
Code : C1
2
3
4
5
6
7 | typedef union mon_union mon_union;
union mon_union
{
int i;
double d;
char str[100];
};
|
Nous aurons donc la possibilité d'associer à notre variable un entier (i), un flottant (d) et une chaîne (str).
Maintenant nous avons besoin de pouvoir connaître le type de la variable en cours d'utilisation, nous devons donc ajouter une valeur, un
int par exemple, mais nous ne pouvons pas l'ajouter à notre union car celle-ci serait écrasée à chaque modification de la valeur dans notre union.
Comment faire ?
La réponse est simple, nous allons créer une structure qui contiendra à la fois notre union et son type.
Voici ce que ça donne :
Code : C1
2
3
4
5
6 | typedef struct Var Var;
struct Var
{
mon_union val;
int type;
};
|
Nous pouvons aussi simplifier ce code en déclarant directement l'union à l'intérieure de la structure (ceci est
facultatif).
Code : C 1
2
3
4
5
6
7
8
9
10
11 | typedef struct Var Var;
struct Var
{
union
{
int i;
double d;
char str[100];
}val;
int type;
};
|
Maintenant pour simplifier le code et éviter les erreurs, nous allons utiliser une énumération pour associer les différents types de notre variable à un nombre :
Code : C1
2
3
4 | enum
{
TYPE_INT,TYPE_DOUBLE,TYPE_STRING
};
|
Voilà, nous en avons fini avec les déclarations, voici donc un petit aperçu de ce que doit contenir notre fichier
union.h :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | #ifndef DEF_VAR_H
#define DEF_VAR_H
typedef struct Var Var;
struct Var
{
union
{
int i;
double d;
char str[100];
}val;
int type;
};
enum
{
TYPE_INT,TYPE_DOUBLE,TYPE_STRING
};
#endif
|
Création de fonctions pour notre structure
Nous devons maintenant créer des fonctions pour associer à notre structure différents types de données, nous passerons notre structure à l'aide des pointeurs.
Association d'un entier
Code : C1
2
3
4
5 | void var_assoc_int(Var* variable, int i)
{
variable->val.i = i;
variable->type = TYPE_INT;
}
|
À la première ligne nous accédons donc à la valeur entière dans l'énumération de notre structure, d'où le
variable->val.i.
Ensuite nous donnons le type TYPE_INT au champs type
(variable->type) de notre structure.
Association d'un flottant
Le code est presque similaire excepté que au lieu d'envoyer un int nous envoyons donc un double à notre fonction, comme ceci :
Code : C1
2
3
4
5 | void var_assoc_double(Var* variable, double d)
{
variable->val.d = d;
variable->type = TYPE_DOUBLE;
}
|
Association d'une chaîne
Maintenant nous allons associer une chaîne à notre variable, nous allons donc utiliser les fonctions de manipulation des chaînes.
Vous devez donc penser à inclure string.h dans le header.
Code : C1
2
3
4
5
6 | void var_assoc_string(Var* variable, const char* chaine)
{
memset(variable->val.str,'\0',100);
strncpy(variable->val.str, chaine, 99);
variable->type = TYPE_STRING;
}
|
À la première ligne nous utilisons la fonction
memset.
Son utilisation est
memset(void* pointeur, int valeur, size_t taille).
Celle-ci se charge de mettre tous les éléments de notre chaîne à
'\0', ceci est pour être sûr que notre chaîne est bien vide avant d'y copier autre chose et pour qu'elle soit véritablement terminée par le caractère final des chaînes qui est le
'\0'.
Ensuite nous utilisons
strncpy qui fait pratiquement la même chose que
strcpy à la différence qu'il prend un paramètre en plus qui détermine le nombre maximum de caractères à copier dans notre chaîne, car ne l'oubliez pas, nous avons utilisé un tableau (
char str[100]) dans notre union, donc notre chaîne peut contenir 99 caractères et le 100ème doit être réservé pour le caractère final.
L'utilisation de strncpy est
strncpy(char* chaîne, const char* copie, size_t maximum)
Et pour finir comme dans les autres fonctions, nous donnons le type voulu à notre variable,
variable->type = TYPE_STRING;.
Et enfin nous allons créer une fonction qui imprimera à l'écran la valeur de notre variable en fonction de son type, pour ce faire, il faut procéder comme suit :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | void var_print(Var* variable)
{
switch(variable->type)
{
case TYPE_INT:
/*valeur entière*/
printf("%d",variable->val.i);
break;
case TYPE_DOUBLE:
/*valeur flottante*/
printf("%lf",variable->val.d);
break;
case TYPE_STRING:
/*chaîne*/
printf("%s",variable->val.str);
break;
default:
/*erreur*/
printf("Erreur : le type de la variable est inconnu!\n");
break;
}
}
|
Dans ce code nous utilisons printf, il faut donc penser à inclure stdio.h dans notre header.
Maintenant, testons un peu ce que nous donne ce bout de code.
Utilisation
Nous allons maintenant tester toutes nos fonctions.
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | #include <stdio.h>
#include <stdlib.h>
#include "var.h"/*obligatoire pour pouvoir connaître notre structure*/
int main(void)
{
Var ma_variable;/*déclaration d'une variable de type Var*/
/*on associe un int*/
var_assoc_int(&ma_variable, 2006);
/*on affiche*/
var_print(&ma_variable);
/*on associe un double*/
var_assoc_double(&ma_variable, 0.123456);
/*on affiche*/
var_print(&ma_variable);
/*on associe une chaîne*/
var_assoc_string(&ma_variable, "Vive les ZeRos !");
/*on affiche*/
var_print(&ma_variable);
return 0;
}
|
Résultat :
Code : Console | 2006
0.123456
Vive les Zér0s !
Press ENTER to continue. |
Voilà, vous avez maintenant les connaissances requises pour utiliser les unions, je vous laisse imaginer tout ce que vous pourrez en faire car les unions sont souvent bien pratiques.
Voici les différents fichiers du projet :
var.h
Code : C 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 | #ifndef DEF_VAR_H
#define DEF_VAR_H
#include <stdio.h>/*pour printf*/
#include <string.h>/*pour strncpy*/
/*un typedef pour éviter d'avoir à preciser le mot struct à chaque
utilisation de nos 'Var'*/
typedef struct Var Var;
/*la structure*/
struct Var
{
union
{
/*valeur entière*/
int i;
/*valeur flottante*/
double d;
/*chaîne d'un maximum de 99 caractères + le caractère final*/
char str[100];
}val;
/*le type en cours d'utilisation*/
int type;
};
/*énumération des différents types*/
enum
{
TYPE_INT,TYPE_DOUBLE,TYPE_STRING
};
/*les prototypes des fonctions*/
void var_assoc_int(Var* variable, int i);
void var_assoc_double(Var* variable, double d);
void var_assoc_string(Var* variable, const char* str);
void var_print(Var* variable);
#endif
|
var.c
Code : C 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 | #include "var.h"
void var_assoc_int(Var* variable, int i)
{
variable->val.i = i;
variable->type = TYPE_INT;
}
void var_assoc_double(Var* variable, double d)
{
variable->val.d = d;
variable->type = TYPE_DOUBLE;
}
void var_assoc_string(Var* variable, const char* chaine)
{
memset(variable->val.str,0,100);
strncpy(variable->val.str, chaine, 99);
variable->type = TYPE_STRING;
}
void var_print(Var* variable)
{
switch(variable->type)
{
case TYPE_INT:
/*valeur entière*/
printf("%d\n",variable->val.i);
break;
case TYPE_DOUBLE:
/*valeur flottante*/
printf("%lf\n",variable->val.d);
break;
case TYPE_STRING:
/*chaîne*/
printf("%s\n",variable->val.str);
break;
default:
/*erreur*/
printf("Erreur : le type de la variable est inconnu!\n");
break;
}
}
|
main.c
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | #include <stdio.h>
#include <stdlib.h>
#include "var.h"/*obligatoire pour pouvoir connaître notre structure*/
int main(void)
{
Var ma_variable;/*déclaration d'une variable de type Var*/
/*on associe un int*/
var_assoc_int(&ma_variable, 2006);
/*on affiche*/
var_print(&ma_variable);
/*on associe un double*/
var_assoc_double(&ma_variable, 0.123456);
/*on affiche*/
var_print(&ma_variable);
/*on associe une chaîne*/
var_assoc_string(&ma_variable, "Vive les ZeRos !");
/*on affiche*/
var_print(&ma_variable);
return 0;
}
|