Enumération (ou enum) en C

26 Aug 2019 26 Aug 2019 29117 vues ESSADDOUKI Mostafa 7 min de lecture

Les énumérations (enum) en C

Une énumération est un type de données défini par l'utilisateur qui permet d'associer des noms symboliques à des constantes entières. Elle améliore la lisibilité du code en remplaçant des valeurs numériques arbitraires par des identifiants expressifs.

Définition — Énumération (enum) Un enum est un type entier dont les valeurs possibles sont représentées par des constantes nommées (les énumérateurs). Par défaut, le premier énumérateur vaut 0, chaque suivant valant le précédent + 1. Les valeurs peuvent être redéfinies explicitement.

Syntaxe — Déclaration d'un enum C
enum NomType { const1, const2, ..., constN };

/* Valeurs explicites (optionnel) */
enum NomType { const1 = val1, const2 = val2, ..., constN = valN };

/* Déclaration de variable */
enum NomType variable;
Aspectenum#defineconst int
Type définiOui — type distinctNon — simple substitution textuelleOui
Regroupement logiqueOui — toutes les constantes dans un typeNonNon
DébogageVisible dans le débogueurInvisible (préprocesseur)Visible
Vérification de typeOui (en C++)NonOui

1. Déclarer un type et des variables enum

Comme pour struct, déclarer un enum crée uniquement un nouveau type — aucune mémoire n'est réservée. Il faut ensuite déclarer une variable de ce type.

MéthodeCode
Déclaration séparéeenum boolean { Vrai, Faux };
enum boolean etat;
Déclaration combinéeenum boolean { Vrai, Faux } etat;
Avec typedeftypedef enum { Vrai, Faux } boolean;
boolean etat;

Exemple n°1 — enum des jours de la semaine

#include <stdio.h>

enum semaine { dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi };
/*             0          1      2      3          4      5         6      */

int main(void)
{
    enum semaine jour;
    jour = mercredi;   /* équivaut à : jour = 3 */

    printf("Numéro du jour            : %d\n", jour);
    printf("Numéro du jour suivant    : %d\n", jour + 1);

    return 0;
}
Sortie
Numéro du jour            : 3
Numéro du jour suivant    : 4
Astuce — typedef enum pour simplifier Avec typedef, plus besoin de répéter le mot-clé enumà chaque déclaration de variable :
typedef enum { dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi } Semaine;

Semaine jour = mercredi;   /* sans répéter "enum" */

2. Valeurs des énumérateurs

a. Valeurs par défaut

Sans initialisation explicite, les énumérateurs reçoivent des valeurs entières consécutives à partir de 0.

Exemple n°2 — Valeurs implicites à partir de 0

#include <stdio.h>

enum semaine { dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi };

int main(void)
{
    printf("dimanche = %d\n", dimanche);   /* 0 */
    printf("lundi    = %d\n", lundi);      /* 1 */
    printf("samedi   = %d\n", samedi);     /* 6 */
    return 0;
}
Sortie
dimanche = 0
lundi    = 1
samedi   = 6

b. Valeurs explicites

On peut affecter des valeurs explicites à certains (ou tous) les énumérateurs. Les suivants non spécifiés reprennent à partir du dernier + 1.

Exemple n°3 — Mélange de valeurs explicites et implicites

#include <stdio.h>

enum etat {
    pret    = 0,
    elu     = 1,
    bloque  = 2,
    zombie  = 3
};

/* Variante : numérotation par puissances de 2 (drapeaux binaires) */
enum droits {
    lecture   = 1,   /* 001 */
    ecriture  = 2,   /* 010 */
    execution = 4    /* 100 */
};

int main(void)
{
    enum etat p1 = elu;
    printf("État de p1       : %d\n",  p1);
    printf("Taille de p1     : %lu octets\n", sizeof(p1));

    /* Combinaison de drapeaux par OR binaire */
    int acces = lecture | ecriture;
    printf("Accès (lire+écrire) : %d\n", acces);

    return 0;
}
Sortie
État de p1       : 1
Taille de p1     : 4 octets
Accès (lire+écrire) : 3
Taille d'un enum En C, un enum est stocké comme un int — il occupe donc généralement 4 octets, quelle que soit la valeur des énumérateurs. C'est ce qui en fait un choix efficace pour les drapeaux (flags) et les états.

c. Deux énumérateurs peuvent partager la même valeur

Exemple n°4 — Alias entre énumérateurs

#include <stdio.h>

enum etat {
    pret   = 0,
    elu    = 0,   /* alias de pret */
    bloque = 1
};

int main(void)
{
    printf("pret   = %d\n", pret);
    printf("elu    = %d\n", elu);
    printf("bloque = %d\n", bloque);

    printf("pret == elu ? %s\n", (pret == elu) ? "Oui" : "Non");

    return 0;
}
Sortie
pret   = 0
elu    = 0
bloque = 1
pret == elu ? Oui

3. enum avec switch

L'usage le plus courant d'un enum est dans une instruction switch : chaque case correspond à un énumérateur nommé, ce qui rend le code immédiatement compréhensible.

Exemple n°5 — Gestion d'états avec enum et switch

#include <stdio.h>

typedef enum {
    PRET    = 0,
    ELU     = 1,
    BLOQUE  = 2,
    ZOMBIE  = 3
} EtatProcessus;

void afficher_etat(EtatProcessus e)
{
    switch (e)
    {
        case PRET   : printf("Processus prêt\n");    break;
        case ELU    : printf("Processus élu\n");     break;
        case BLOQUE : printf("Processus bloqué\n");  break;
        case ZOMBIE : printf("Processus zombie\n");  break;
        default     : printf("État inconnu\n");
    }
}

int main(void)
{
    EtatProcessus p = BLOQUE;
    afficher_etat(p);

    p = ELU;
    afficher_etat(p);

    return 0;
}
Sortie
Processus bloqué
Processus élu

4. Règles importantes

a. Unicité dans la portée

Tous les énumérateurs doivent avoir des noms uniques dans leur portée. Deux enum différents ne peuvent pas avoir un énumérateur de même nom dans la même portée.

Exemple n°6 — Conflit de noms entre deux enum

enum state  { working, failed };
enum result { failed,  passed };   /* erreur : "failed" redéfini */

int main(void) { return 0; }
Sortie
error: redefinition of enumerator 'failed'
Danger — Collision d'énumérateurs dans la même portée Si deux enum définis dans la même portée partagent un nom d'énumérateur, le compilateur signale une erreur. Solutions : renommer les énumérateurs pour les rendre uniques (convention : préfixer par le nom du type) ou utiliser des portées distinctes (namespaceen C++).
/* Convention de nommage recommandée en C */
enum State  { STATE_WORKING,  STATE_FAILED  };
enum Result { RESULT_FAILED,  RESULT_PASSED };

b. Les valeurs doivent être des constantes entières

Attention — Valeurs flottantes ou expressions non constantes interdites Les énumérateurs ne peuvent recevoir que des constantes entièresconnues à la compilation. Les expressions flottantes, les variables ou les appels de fonctions sont interdits :
int x = 5;
enum T { A = x };       /* erreur — x n'est pas une constante */
enum T { B = 3.14 };    /* erreur — valeur flottante          */
enum T { C = 2 + 3 };   /* autorisé — expression constante    */

Récapitulatif

ConceptRègle / SyntaxePoint clé
Déclarationenum Nom { A, B, C };Crée un type — pas de mémoire allouée
Valeurs par défautA=0, B=1, C=2…Consécutives à partir de 0
Valeurs explicitesenum Nom { A=5, B, C=10 };B vaut 6 (précédent + 1)
Taillesizeof(enum Nom) = 4Stocké comme un int
Alias autoriséA=0, B=0Deux noms peuvent partager la même valeur
Unicité des nomsNoms uniques dans la portéeConflit → erreur de compilation
ConventionPREFIX_NOMÉvite les conflits entre plusieurs enum
Cas d'usage principalswitch(var) { case A: … }Code lisible sans constantes numériques brutes

Discussion (0)

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Votre commentaire sera visible après modération.