81 votes

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

Pourquoi cela:

 #include <stdio.h>
#include <limits.h>
#include <inttypes.h>

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

print 4 8 8 en C et 8 8 8 en C ++ (sur une plate-forme de 4 octets)?

J'avais l'impression que l'assignation UINT64_MAX forcerait toutes les constantes d'énumération à au moins 64 bits, mais en_e_foo reste à 32 en C simple.

Quelle est la raison de la divergence?

80voto

Keith Thompson Points 85120

En C, une enum constante est de type int. En C++, c'est du type énuméré.

enum en_e{
    en_e_foo,
    en_e_bar=UINT64_MAX,
};

En C, c'est une violation de contrainte, nécessitant un diagnostic (si UINT64_MAX dépasse INT_MAX, ce qui très probablement n'). Un compilateur C peut rejeter le programme entièrement, ou il peut imprimer un avertissement, puis générer un exécutable dont le comportement est indéfini. (Il n'est pas 100% clair qu'un programme qui viole une contrainte a nécessairement un comportement indéfini, mais dans ce cas, le standard ne veut pas dire que le comportement est, donc c'est toujours un comportement non défini.)

gcc 6.2 ne pas vous avertir. clang. C'est un bug de gcc; il inhibe de façon incorrecte certains messages de diagnostic en utilisant des macros à partir des en-têtes standard sont utilisés. Grâce à Grzegorz Szpetkowski pour localiser le rapport de bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71613

En C++, chaque type d'énumération est un type sous-jacent, qui est un certain type entier (pas nécessairement int). Ce type sous-jacent doit être capable de représenter toutes les valeurs de constante. Donc, dans ce cas, les deux en_e_foo et en_e_bar sont de type en_e, qui doit être d'au moins 64 bits, même si int est plus étroite.

25voto

Ben Voigt Points 151460

Ce code n'est pas valide C dans la première place.

Section 6.7.2.2 dans les deux C99 et C11 dit que:

Contraintes:

L'expression qui définit la valeur d'une constante d'énumération doit être une expression constante entière qui a une valeur représentable comme un int.

Un compilateur de diagnostic est obligatoire parce qu'il est une violation de contrainte, voir 5.1.1.3:

Une mise en œuvre conforme doit produire au moins un message de diagnostic (identifié la mise en œuvre définis) si un prétraitement de l'unité de traduction ou de l'unité de traduction contient une violation de toute règle de syntaxe ou de contrainte, même si le comportement est également explicitement spécifié non définies ou définis par l'implémentation.

23voto

HolyBlackCat Points 2137

Dans C, alors qu'un enum est considéré comme un type distinct, les agents recenseurs lui-même toujours le type int.

C11 - 6.7.2.2 Énumération des prescripteurs

3 Les identificateurs dans un énumérateur de la liste sont déclarés comme des constantes de type int...

Ainsi, le comportement que vous voyez est un compilateur extension.

Je dirais qu'il fait sens pour agrandir la taille de l'un des agents recenseurs si sa valeur est trop grande.


D'autre part, en C++ tous les agents recenseurs ont le type de l' enum elles sont déclarées.

À cause de cela, la taille de chaque agent recenseur doit être la même. Donc, la taille de l'ensemble de l' enum est étendu à stocker le plus grand agent recenseur.

16voto

Grzegorz Szpetkowski Points 10225

Comme d'autres ont fait, le code est mal formé (en C), en raison de violation de contrainte.

Il est GCC bug #71613 (signalé en juin 2016), qui indique que des avertissements utiles sont réduits au silence avec des macros.

Avertissements utiles semblent être réduits au silence lorsque des macros à partir du système d'en-têtes sont utilisés. Par exemple, dans l'exemple ci-dessous un avertissement serait utile pour les deux énumérations, mais seulement un message d'avertissement est affiché. La même chose peut probablement se produire pour d'autres mises en garde.

La solution actuelle peut être à faire précéder de la macro avec unaire + opérateur:

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

ce qui donne d'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 restricts enumerator values to range of ‘int' [-Wpedantic]
         en_e_bar = +UINT64_MAX

12voto

haccks Points 33022

C11 - 6.7.2.2/2

L'expression qui définit la valeur d'une constante d'énumération doit être une expression constante entière qui a une valeur représentable comme un int.

en_e_bar=UINT64_MAX est une violation de contrainte et de ce fait, le code ci-dessus invalide. Un message de diagnostic devrait être produit par la confirmation de la mise en œuvre comme indiqué dans le C11 projet:

Une mise en œuvre conforme doit produire au moins un message de diagnostic (identifié la mise en œuvre définis) si un prétraitement de l'unité de traduction ou de l'unité de traduction contient une violation de toute règle de syntaxe ou de contrainte, [...]

Il semble que la GCC a quelques bug et il a échoué à produire le message de diagnostic. (Le Bug est signalé dans la réponse par Grzegorz Szpetkowski

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X