Enum constantes se comportant différemment en C et C ++

Pourquoi cela:

#include  #include  #include  int main() { enum en_e { en_e_foo, en_e_bar = UINT64_MAX, }; enum en_e e = en_e_foo; printf("%zu\n", sizeof en_e_foo); printf("%zu\n", sizeof en_e_bar); printf("%zu\n", sizeof e); } 

imprimer 4 8 8 en C et 8 8 8 en C ++ (sur une plate-forme avec 4 octets)?

J’avais l’impression que l’atsortingbution UINT64_MAX forcerait toutes les constantes d’énumération à au moins 64 bits, mais en_e_foo rest à 32 dans la plaine C.

Quelle est la raison de cette divergence?

En C, une constante enum est de type int . En C ++, il s’agit du type énuméré.

 enum en_e{ en_e_foo, en_e_bar=UINT64_MAX, }; 

Dans C, il s’agit d’une violation de contrainte , nécessitant un diagnostic ( si UINT64_MAX dépasse INT_MAX , ce qu’il fait très probablement). Le compilateur AC peut rejeter complètement le programme ou il peut imprimer un avertissement, puis générer un exécutable dont le comportement n’est pas défini. (Il n’est pas clair à 100% qu’un programme qui viole une contrainte a nécessairement un comportement indéfini, mais dans ce cas, la norme ne dit pas quel est le comportement, donc c’est toujours un comportement indéfini.)

gcc 6.2 ne le prévient pas. clang fait. Ceci est un bug dans gcc; il inhibe de manière incorrecte certains messages de diagnostic lorsque des macros provenant des en-têtes standard sont utilisées. Merci à Grzegorz Szpetkowski d’avoir localisé le rapport de bogue: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71613

En C ++, chaque type d’énumération a un type sous-jacent , qui est un type entier (pas nécessairement int ). Ce type sous-jacent doit pouvoir représenter toutes les valeurs constantes. Donc, dans ce cas, en_e_foo et en_e_bar sont tous deux de type en_e , qui doivent avoir au moins 64 bits de large, même si int est plus étroit.

Ce code n’est tout simplement pas valide en premier lieu.

La section 6.7.2.2 en C99 et C11 indique que:

Contraintes:

L’expression qui définit la valeur d’une constante d’énumération doit être une expression de constante entière dont la valeur peut être représentée par un int .

Un diagnostic du compilateur est obligatoire car il s’agit d’une violation de contrainte, voir 5.1.1.3:

Une implémentation conforme doit produire au moins un message de diagnostic (identifié de manière définie par l’implémentation) si une unité de traduction ou une unité de traduction de prétraitement enfreint une règle ou une contrainte de syntaxe, même si elle est explicitement définie comme non définie ou implémentée. défini.

En C , alors qu’un enum est considéré comme un type distinct, les énumérateurs eux-mêmes ont toujours le type int .

C11 – 6.7.2.2 Spécificateurs d’énumération

3 Les identifiants d’une liste d’énumérateurs sont déclarés comme des constantes de type int …

Ainsi, le comportement que vous voyez est une extension du compilateur.

Je dirais qu’il est logique d’étendre seulement la taille de l’un des énumérateurs si sa valeur est trop grande.


Par contre, en C ++, tous les énumérateurs ont le type de enum lequel ils sont déclarés.

Pour cette raison, la taille de chaque énumérateur doit être la même. Ainsi, la taille de l’ enum complet est étendue pour stocker le plus grand énumérateur.

Comme d’autres l’ont souligné, le code est mal formé (en C), à cause de la violation des contraintes.

Il y a le bogue GCC n ° 71613 (rapporté en juin 2016), qui indique que certains avertissements utiles sont réduits au silence avec les macros.

Les avertissements utiles semblent être réduits au silence lorsque les macros des en-têtes système sont utilisées. Par exemple, dans l’exemple ci-dessous, un avertissement serait utile pour les deux énumérations, mais un seul avertissement est affiché. La même chose peut probablement arriver pour d’autres avertissements.

La solution de contournement actuelle peut être d’append la macro à un opérateur unaire + :

 enum en_e { en_e_foo, en_e_bar = +UINT64_MAX, }; 

qui génère une erreur de compilation sur ma machine avec GCC 4.9.2:

 $ gcc -std=c11 -pedantic-errors -Wall main.c main.c: In function 'main': main.c:9:20: error: ISO C ressortingcts enumerator values to range of 'int' [-Wpedantic] en_e_bar = +UINT64_MAX 

C11 – 6.7.2.2/2

L’expression qui définit la valeur d’une constante d’énumération doit être une expression de constante entière ayant une valeur représentable sous la forme d’un int .

en_e_bar=UINT64_MAX est une violation de contrainte, ce qui rend le code ci-dessus invalide. Un message de diagnostic doit être produit en confirmant la mise en œuvre indiquée dans le projet C11:

Une implémentation conforme doit produire au moins un message de diagnostic (identifié d’une manière définie par l’implémentation) si une unité de traduction ou une unité de traduction de prétraitement contient une violation d’une règle ou d’une contrainte de syntaxe, […]

Il semble que GCC ait un bug et qu’il n’a pas réussi à produire le message de diagnostic. (Bug est indiqué dans la réponse par Grzegorz Szpetkowski

J’ai jeté un coup d’oeil aux normes et mon programme semble être une violation de contrainte en C à cause du 6.7.2.2p2 :

Contraintes: L’expression qui définit la valeur d’une constante d’énumération doit être une expression de constante entière dont la valeur peut être représentée par un int.

et défini en C ++ à cause de 7.2.5:

Si le type sous-jacent n’est pas fixe, le type de chaque énumérateur est le type de sa valeur d’initialisation: – Si un initialiseur est spécifié pour un énumérateur, la valeur d’initialisation a le même type que l’expression et l’expression constante doit être une intégrale expression constante (5.19). – Si aucun initialiseur n’est spécifié pour le premier énumérateur, la valeur d’initialisation a un type intégral non spécifié. – Sinon, le type de la valeur d’initialisation est identique au type de la valeur d’initialisation de l’énumérateur précédent, sauf si la valeur incrémentée n’est pas représentable dans ce type, auquel cas le type est un type intégral non spécifié pour contenir la valeur incrémentée. Si ce type n’existe pas, le programme est mal formé.