Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Différence entre #define et enum

39 réponses
Avatar
Vincent Belaïche
Bonjour,

Dans la Foire à Questions du groupe on trouve
http://www.levenez.com/lang/c/faq/fclc006.html#q_8

6.8 Quelle est la différence entre une énumération et des #define ?

Il y a peu de différences.

L'un des avantages de l'énumération est que les valeurs numériques sont
assignées automatiquement. De plus, une énumération se manipule comme un
type de données. Certains programmeurs reprochent aux énumérations de
réduire le contrôle qu'ils ont sur la taille des variables de type
énumération.


Il me semble qu'il y a d'autres différences notables:

1) avec les #define on peut tester une valeur entière pendant le
prétraitement

#define TOTO 1

#if TOTO == 1
...
#elif TOTO == 2
...
#else
#endif

(même si le code ci-dessus peut ne pas plaire pour diverse raison, c'est
très utilisé)
Le code ci-dessus ne marche pas si TOTO est un enum

2) Les enum sont en général dispo sous forme symbolique dans un
débogueur qui se respecte, pas les #define

3) Par rapport au commentaire sur la taille non controllée avec les
énum, il me semble qu'on peut qualifier un enum avec `: taille_en_bit'
au sein d'un typedef, un peu comme les champ d'une structure. À
vérifier, et aussi à quelle norme ça répond.

Vincent.

9 réponses

1 2 3 4
Avatar
Jean-Marc Bourguet
Samuel DEVULDER writes:

Antoine Leca a écrit :

Le type (c'est quoi en fait ?) est une énumération, et on s'en moque la
plupart du temps. Les membres de l'énumération créent automatiquement
des constantes de même nom, qui sont des int avec la valeur sous-jacente.
Sauf extension du compilateur qui permet d'avoir autre chose que int.


enum {LUNDI, MARDI, MERCREDI=3u, JEUDI, ...}. Le MERCREDI est unsigned,
mais pas MARDI ou JEUDI?



MERCREDI est signé en C normal (et vaut 3, 2 de plus que MARDI).



Ah ok.. Pourtant le compilo HP dit que les enums sont "unsigned int" sur
64bits et "signed int" en 32. Si sizeof(int)=4, et sizeof(long)=8, avec:
enum {A=0xffffffff} a = A; long b = a;
on devrait avoir b = 0xffffffff ffffffff en 32bits et
0x00000000 ffffffff en 64?
Ca fait bizarre. C'est peut-être une extension non standard le fait qu'ils
considèrent les enums en unsigned sous 64bits. Je sais pas.



Les *constantes* ont un type int. (Citations de C99, C90 ne diffère pas
fondamentalement)

The identifiers in an enumerator list are declared as constants that have
type int and may appear wherever such are permitted.

Mais ça ne dit rien sur les variables. Pour ça on a

Each enumerated type shall be compatible with char, a signed integer type,
or an unsigned integer type. The choice of type is implementation-defined,
but shall be capable of representing the values of all the members of the
enumeration.

A+

--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
Samuel DEVULDER
Antoine Leca a écrit :
Samuel DEVULDER écrivit :
Ah ok.. Pourtant le compilo HP dit que les enums sont "unsigned int" sur
64bits



Quelle idée bizarre.



Ben ouais.. Quand je suis tombé sur la doc, ca m'a surpris car
intuitivement j'ai toujours appris que enum == int, et que sans
précision de signe l'int est signé. Mais c'est peut-être pas une règle
absolue, vu que pour les chars, ca dépend du compilo et de ses options.

Autrement dit, avec un tel compilateur :

enum jours jour;
/* ... */
/* recule dans la semaine jusqu'au week end */
while( --jour >= 0 ) /*...*/

donne un résultat différent suivant la taille des entiers...



C'est pas la taille qui compte (dans les deux cas sizeof(int)==4) mais
le fait qu'on considère qu'un enum est signé ou pas.

J'ai pas dit que c'était bô :-)



Ouais. Le truc bizzarre est peut être de considérer qu'enum est toujours
une valeur signée? Un rapide grep sur la norme ne semble indiquer aucune
valeur de signe n'est indiquée mais uniquement le fait qu'il faut
représenter l'enum par un truc assez grand pour y stocker un int.

mais c'est en tous cas quelque chose qui
va fonctionner sur la plupart des machines... sauf ce compilo-ci.



Et pourtant, c'est le compilo C du fabriquant.

"HP-UX 64-bit compiler and linker changes",
http://1007.www2.hp.com/portal/site/dspp/menuitem.863c3e4cbcdc3f3515b49c108973a801?ciidG27276391695110VgnVCM100000275d6e10RCRD#hpc
==> regarder l'option "+se"

J'ai du mal à croire qu'ils prennent des libertés avec la norme.
J'imagine que c'est juste la norme qui ne précise rien et qu'ils ont
choisi le truc qui les arrangeait (en même temps je ne sais pas en quoi
c'est plus performant d'avoir des enum signés ou pas).

Ca dépend du cas par cas. Cela dit je trouve PLUVIOSITE10/30 bizarre.



C'est PLUVIOSE10 / 30. PLUVIOSE10 représentant le 10 pluviôse (en C on
ne peut pas commencer par un chiffre).



Oops j'avais mal lu. J'ai cru qu'on parlait de mm de flotte[*] dans le
mois : pluviosité.
__
[*] dans le pastis... ceci explique peut être celà :)


Et le calendrier révolutionnaire
avait des mois tous de 30 jours, donc l'expression ci-dessus donne le
mois, ici pluviôse. De même, en divisant par 10 tu obtiens la «dimaine»,
l'équivalent de la semaine. Toutes chose que que tu ne peux pas faire



Ah le fameux système décimal universel.

avec le calendrier grégorien, en tous cas pas aussi facilement
(je connais Zeller).



Jacques? Jacques Zeller... vrooooom! (lol)

Bref, c'est une anecdote. Si on revenais au C ?



Oui, un peu de sérieux, il y a des officiels qui nous regardent.

sam.
Avatar
Jean-Marc Bourguet
Antoine Leca writes:

Samuel DEVULDER écrivit :
Ah ok.. Pourtant le compilo HP dit que les enums sont "unsigned int" sur
64bits



Quelle idée bizarre.
Autrement dit, avec un tel compilateur :

enum jours jour;
/* ... */
/* recule dans la semaine jusqu'au week end */
while( --jour >= 0 ) /*...*/

donne un résultat différent suivant la taille des entiers...
J'ai pas dit que c'était bô :-) mais c'est en tous cas quelque chose qui
va fonctionner sur la plupart des machines... sauf ce compilo-ci.



En passant, je ne suis pas sûr que ce que j'ai cité dans un autre message
(6.7.2.2/4 de C99) soit suffisant pour l'autoriser.

A+

--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
Jean-Marc Bourguet
Antoine Leca writes:

C'est PLUVIOSE10 / 30. PLUVIOSE10 représentant le 10 pluviôse (en C on
ne peut pas commencer par un chiffre). Et le calendrier révolutionnaire
avait des mois tous de 30 jours, donc l'expression ci-dessus donne le
mois, ici pluviôse. De même, en divisant par 10 tu obtiens la «dimaine»,



décade (10 jours mais ce mot a tendance à prendre le sens de décennie en
prime, peut-être sous l'influence de l'anglais)

--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
Samuel DEVULDER
Jean-Marc Bourguet a écrit :

Les *constantes* ont un type int. (Citations de C99, C90 ne diffère pas
fondamentalement)

The identifiers in an enumerator list are declared as constants that have
type int and may appear wherever such are permitted.



int signées ou pas? Sans précision c'est signé j'imagine.


Mais ça ne dit rien sur les variables. Pour ça on a

Each enumerated type shall be compatible with char, a signed integer type,
or an unsigned integer type. The choice of type is implementation-defined,
but shall be capable of representing the values of all the members of the
enumeration.



Ah effectivement, merci. Ca laisse bcp de libertés. Le truc est peut
être qu'on ne devrait simplement pas pouvoir déclarer des variables de
type enum et n'utiliser ces dernier que sous forme de constantes, ou ce
qui revient au même interdire l'arithmétique sur une variable enum.

Si on tient à faire de l'arithmetique sur un num, il faut passer par une
conversion explicite vers une variable entière, y faire le calcul et en
retour convertir l'entier en enum.

enum jour = PLUVIOSE10; // constante
int var_int = jour; // on passe dans le monde "entier": calculs acceptés
int result_int = 2*var_int - 4; // calcul entier de base
enum result_jour = result_int; // conversion int => enum

(après c'est le compilo qui se débrouille pour optimiser tout cela, et
faire les extensions de signes là ou il faut).

sam.
Avatar
Manuel Pégourié-Gonnard
Jean-Marc Bourguet scripsit :

Manuel Pégourié-Gonnard writes:
En fait, est-il exact de dire que les deux fragments de code suivants
sont équivalents ?

typedef enum {
a = 1,
b,
} foo;

et

const int a = 1;
const int b = 2;
typedef const int foo;



Certainement pas avec un const.

La différence pratique la plus importante que je vois est que a et b dans
le second cas ne peuvent pas être utilisés dans les expressions entières
constantes.



En effet, c'est un point important qui m'avait échappé. Dans le premier
cas, on a le droit par exemple de dire

int tab[b];

mais pas dans le deuxième (sauf nouvelle erreur de ma part). C'est bien
ce que tu voulais dire ?

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Avatar
Vincent Lefevre
Dans l'article <4bbe51e0$0$2972$,
Richard Delorme écrit:

Le 08/04/2010 23:47, Manuel Pégourié-Gonnard a écrit :
> En fait, est-il exact de dire que les deux fragments de code suivants
> sont équivalents ?

Non.

>
> typedef enum {
> a = 1,
> b,
> } foo;

Ici a et b n'ont pas d'adresses.

> const int a = 1;
> const int b = 2;
> typedef const int foo;

Ici ils en ont une.



Autre différence: le type de foo n'est pas forcément un int
(en revanche, a et b doivent bien être des int).

Il me semble d'ailleurs y avoir un defect dans la norme, qui
autorise par exemple foo à être compatible avec un unsigned long
(à moins que j'ai raté quelque chose), ce qui ne correspond pas
à ce que dit le rationale:

6.4.4.3 Enumeration constants

Whereas an enumeration variable may have any integer type that
correctly represents all its values when widened to int, an
enumeration constant is only usable as the value of an expression.
Hence its type is simply int.

Noter le "widened". Je pense que le but était d'autoriser un
enum à être compatible avec char (si signé ou < int), signed char,
unsigned char (si < int), short, et unsigned short (si < int) pour
qu'une variable de type enum prenne moins de place en mémoire.

--
Vincent Lefèvre - Web: <http://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / Arénaire project (LIP, ENS-Lyon)
Avatar
Jean-Marc Bourguet
Manuel Pégourié-Gonnard writes:

Jean-Marc Bourguet scripsit :

En effet, c'est un point important qui m'avait échappé. Dans le premier
cas, on a le droit par exemple de dire

int tab[b];

mais pas dans le deuxième (sauf nouvelle erreur de ma part). C'est bien
ce que tu voulais dire ?



Oui (en C90, en c99 on a les VLA et gcc a une extension qui en donne aussi
en C90).

A+

--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
Jean-Marc Bourguet
Samuel DEVULDER writes:

Jean-Marc Bourguet a écrit :

Les *constantes* ont un type int. (Citations de C99, C90 ne diffère pas
fondamentalement)

The identifiers in an enumerator list are declared as constants that have
type int and may appear wherever such are permitted.



int signées ou pas? Sans précision c'est signé j'imagine.



Oui.

Mais ça ne dit rien sur les variables. Pour ça on a

Each enumerated type shall be compatible with char, a signed integer type,
or an unsigned integer type. The choice of type is implementation-defined,
but shall be capable of representing the values of all the members of the
enumeration.



Ah effectivement, merci. Ca laisse bcp de libertés. Le truc est peut être
qu'on ne devrait simplement pas pouvoir déclarer des variables de type enum
et n'utiliser ces dernier que sous forme de constantes, ou ce qui revient
au même interdire l'arithmétique sur une variable enum.

Si on tient à faire de l'arithmetique sur un num, il faut passer par une
conversion explicite vers une variable entière, y faire le calcul et en
retour convertir l'entier en enum.



Comme je l'ai écrit par ailleurs, passer de "être compatible" au
comportement dans des expressions, le saut est un peu grand pour moi. Mais
j'ai encore rien trouvé dans un sens ou dans l'autre. (Mais je n'en suis
pas encore à me demander s'il ne faudrait pas un DR, simplement je n'ai pas
trop le temps de chercher).

A+

--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
1 2 3 4