web-dev-qa-db-fra.com

Pourquoi deux constantes d'énumération d'énumération différentes peuvent-elles avoir la même valeur entière?

Je sais que si j'ai défini un jour de semaine enum comme celui-ci:

enum weekday {
    MON,
    TUE,
    WED,
    THU,
    FRI,
};

Ensuite, MON serait en interne égal à 0, par défaut, et TUE à 1, WED à 2 ...

Mais si je le définis de cette façon:

enum weekday {
    MON,
    TUE = 0,
    WED,
    THU,
    FRI,
};

Ensuite, MON et TUE obtiendraient la valeur 0.

Comment un système différencierait-il MON et AUT en interne? Je veux dire, si je déclare quelque chose comme ça:

enum weekday today = 0;

Alors, c'est aujourd'hui MON ou TUE? Ou, philosophiquement parlant, les deux?

43
Xu Hong

Les énumérations C sont des "véritables" entiers - non seulement parce qu'elles sont implémentées de cette façon, mais parce que les types d'énumération standard définit doivent avoir des valeurs entières. Ainsi, la valeur de today est "vraiment" 0. Tout ce qui s'est passé, c'est que vous avez créé deux noms différents pour la valeur 0.

Je suppose alors que la réponse à "est aujourd'hui MON ou MAR" est "oui" ;-)

La langue ne vous arrête pas car il est parfois utile pour une énumération d'avoir plusieurs noms pour la même valeur. Par exemple:

enum compression_method {
    COMP_NONE = 0,
    COMP_LOW = 1,
    COMP_HIGH = 2,
    COMP_BEST = 2,
    COMP_FASTEST = 0,
};
66
Steve Jessop

Pourquoi deux constantes d'énumération différentes peuvent-elles avoir la même valeur entière?

Parce qu'il est explicitement autorisé par le projet standard N1265 C99 au 6.7.2.2/3 "Spécificateurs d'énumération":

L'utilisation d'énumérateurs avec = Peut produire des constantes d'énumération avec des valeurs qui dupliquent d'autres valeurs dans la même énumération.

Comment un système différencierait-il MON et TUE en interne?

Je pense que c'est impossible car ce sont des constantes de temps de compilation (6.6/6 "Expressions constantes"). En conséquence, ils:

  • ne peut pas être modifié pour les différencier après la compilation

  • n'ont pas d'adresse pour les différencier: Emplacement mémoire de la valeur d'énumération en C

    Les constantes de temps de compilation n'ont besoin d'aucune adresse car les adresses sont inutiles pour des choses que vous ne pouvez pas modifier.

GCC remplace simplement l'utilisation des membres enum par des valeurs immédiates dans Assembly au moment de la compilation. Considérer:

#include <stdio.h>

enum E {
    E0 = 0x1234,
    E1 = 0x1234
};
int i = 0x5678;

int main() {
    printf("%d\n", E0);
    printf("%d\n", E1);
    printf("%d\n", i);
    return 0;
}

Compilez et décompilez avec GCC 4.8 x86_64:

gcc -c -g -O0 -std=c89 main.c
objdump -Sr main.o

La sortie contient:

    printf("%d\n", E0);
   4:       be 34 12 00 00          mov    $0x1234,%esi
  ...
    printf("%d\n", E1);
  18:       be 34 12 00 00          mov    $0x1234,%esi
  ...
    printf("%d\n", i);
  2c:       8b 05 00 00 00 00       mov    0x0(%rip),%eax        # 32 <main+0x32>
                    2e: R_X86_64_PC32       i-0x4
  32:       89 c6                   mov    %eax,%esi

On voit donc que:

  • les membres enum sont utilisés comme intermédiaires $0x1234, il est donc impossible de savoir d'où ils viennent
  • la variable i provient cependant de la mémoire 0x0(%rip) (à déplacer), donc deux variables pourraient être différenciées par adresse

Pour compléter d'autres réponses, je vais vous donner un exemple pratique de la façon dont l'utilisation de la même valeur pour différentes énumérations sur un enum donné est largement utile:

enum slots_t {
    SLOT_FIRST = 0,
    SLOT_LEFTARM = SLOT_FIRST,
    SLOT_RIGHTARM = 1,
    SLOT_TORSO = 2,
    SLOT_LEFTLEG = 3,
    SLOT_RIGHTLEG = 4,
    SLOT_LAST = SLOT_RIGHTLEG
};

Ensuite, vous pouvez faire dans votre code:

for (int i = SLOT_FIRST; i <= SLOT_LAST; ++i) { }
10
Alexandre Severino

C'est aussi philosophique (ou pas) que

#define ZILCH 0
#define NADA  0

Il existe de nombreuses utilisations où il est logique que différents noms donnent le même numéro.

8
Jens

Le nom de la constante d'énumération est utilisé pour affecter la valeur et non la valeur réelle elle-même. Si vous affectez la valeur 0 à aujourd'hui, la valeur de sortie sera 0. Et oui, MON et TUE auront la valeur 0 et les autres auront la valeur WED = 1 THU = 2 et ainsi de suite.

2
heretolearn