Aller au menu - Aller au contenu

[Plan du site] Vous êtes ici --- > Le Site du Zéro > Les tutoriels > Non-Officiels > Programmation > C > Les unions > Lecture du tutoriel

Les unions

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)
Avatar
Auteur : slimshady451
Note : 19 / 20 (3 votes)
Visualisations : 7 261

Plus d'informations Plus d'informations
Salut à tous amis zéros.
Dans ce tuto je vais tenter de vous expliquer l'utilisation des unions en C, les unions sont très proches des structures dans leur utilisation mais en sont bien différentes.
C'est partit ! :pirate:
Sommaire du chapitre :
Icône du chapitre

Déclaration d'une union et utilisation

Déclaration



Une union se déclare de la même façon qu'une structure, comme ceci :

Code : C
1
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 : C
1
2
3
4
5
int main(void)
{
    union MonUnion variable;
    return 0;
}


Mais c'est la même chose qu'une structure ton union ?! :diable:


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à !? :o


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 ? o_O


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. :diable:


Mais non. :p
Ç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.

Utilisation d'union avec les arguments variables

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 : C
1
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 : C
1
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 ? o_O


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 : C
1
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.

Des variables intelligentes

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 : C
1
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 : C
1
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 : C
1
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 : C
1
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 : C
1
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 : C
1
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;
}


Là je vous dois une petite explication. ^^


À 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. :D

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;
}

Q.C.M.

Une union est identique à une structure :
Est-ce que je peux ajouter une union à l'intérieure d'une structure ?
Mon union comporte 3 champs de type int, et un de type char, un int fait 4 octets et un char en fait 1, quelle est la taille de mon union ?

Statistiques de réponses au QCM


Après avoir lu le dernier chapitre, vous pouvez améliorer quelques points vous-même au projet :



Voilà, bonne lecture. :)
Retour en haut Retour en haut


Créé : le 15/09/2006 à 10:17:19
Modifié : le 22/08/2008 à 16:08:09
Avancement : 100%
Licence : Copie non autorisée

Changer de design | En savoir plus | Plan du site | Politique d'accessibilité | Règles | Fil RSS | XHTML 1.0 | CSS 2.0
Édité par Simple IT SARL : Nous contacter | Revue de presse | Publicité

Y'a plus rien à lire, faut remonter maintenant !

Hébergement web - Correction de tutoriels - Créer un site
Vous souhaitez apparaître ici ? Contactez-nous.

Nombre de connectés 439 Zéros connectés | Requêtes SQL 9 requêtes | Temps de génération de la page : Total (SQL) 0.0425s (0.0314s)