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.

10 réponses

1 2 3 4
Avatar
-ed-
On 7 avr, 08:38, Vincent Belaïche wrote:
Bonjour,

Dans la Foire à Questions du groupe on trouvehttp://www.levenez.com/lan g/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 com me 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'es t
très utilisé)
Le code ci-dessus ne marche pas si TOTO est un enum



Oui.

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



Oui.

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.



Non. Par contre, avec les constantes numériques, on peut préciser le
type :

.f : float
. : double
u : unsigned
l : long
etc.
Avatar
Jean-Marc Bourguet
Vincent Belaïche writes:

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
2) Les enum sont en général dispo sous forme symbolique dans un débogueur
qui se respecte, pas les #define



Oui.

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.



Aucune à ma connaissance. C++0X introduit la possibilité de donner le type
sousjacent avec une syntaxe similaire, mais pas de taille en bit.

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
Blue Prawn
Vincent Belaïche wrote:

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.



il y a aussi un warning généré si un switch est incomplet avec les enums.
Avatar
Mickaël Wolff
Blue Prawn a écrit :

il y a aussi un warning généré si un switch est incomplet avec les enums.



Si ton compilateur a cette fonctionnalité, ce n'est pas le cas de
celui que j'ai sous la main. Bref, ça n'a rien à voir avec le C.

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Avatar
Samuel DEVULDER
Vincent Belaïche a écrit :

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.



En corolaire: Comme les énums sont a priori des valeurs signées (vu que
je peux avoir un énum qui vaut -1 par ex), est-ce que le compilo qui
accepterait le ":bits" grognerait si on a oublié d'y mettre un bit
supplémentaire pour les valeurs négatives?

Truc curieux avec le compilo C de HP sur les enums. Si on compile en
mode 32bit, alors ils sont signés. En 64bits il ne le sont pas, sauf si
on ajoute l'option "+se".

Du coup, si les enums sont des "int" avec choix libre laissé au compilo
du signe, peut on pour autant faire de l'arithmétique simple avec eux
sans risquer des soucis?

typedef enum {
LUNDI, MARDI, MERCREDI, JEUDI, VENDREDI, SAMEDI, DIMANCHE
} t_jour;

/* retourne vrai si j1 et j2 sont au plus séparés de 3 jours consécutifs */
int fenetre_3_jours(t_jour j1, t_jour j2) {
int delta = j1 - j2;
return -3<delta && delta<3;
}

Que donne fenetre_3_jours(LUNDI, MARDI) compilé sur machine HP 32bits ou
64bits?

Si on s'interdit la soustraction des enums, l'écriture de
fenetre_3_jours sera plus laborieuse (et peut être bugée):

return (j1==LUNDI && (j2<=MERCREDI || j2>=SAMEDI)) ||
(j1==MARDI && (j2<=JEUDI || j2==DIMANCHE)) ||
(j1==MERCREDI && (j2<=VENDREDI)) ||
(j1==JEUDI && (j2>=MARDI && j2<=SAMEDI)) ||
(j1==VENDREDI && (j2>=MERCREDI)) ||
(j1==SAMEDI && (j2>=JEUDI || j2==LUNDI) ||
(j1==DIMANCHE && (j2>=VENDREDI || j2<=MARDI);

sam.
Avatar
Vincent Belaïche
Au temps pour moi,

en effet, maintenant ça me revient, il s'agissait de spécifier le type
sousjacent et non d'un spécificateur en nombre de bits, mais c'était
fait sans C++0X de la façon suivante (attention j'ignore si ce code est
correct ou non, c'est qq chose que j'ai vu faire):

------------------------
enum couleurs {
rouge, vert, bleu
};
typedef char couleurs;
------------------------

Par la suite on utilise couleurs comme le nom de type (et nom `enum
couleurs')

Ça compile sans aucun conflit de nom puisque dans la première
déclaration couleurs n'est pas un nom de type.

J'aimerais savoir si un tel code est portable et à l'épreuve du temps.

Vincent.


Jean-Marc Bourguet a écrit :
Vincent Belaïche writes:

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
2) Les enum sont en général dispo sous forme symbolique dans un débogueur
qui se respecte, pas les #define



Oui.

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.



Aucune à ma connaissance. C++0X introduit la possibilité de donner le type
sousjacent avec une syntaxe similaire, mais pas de taille en bit.

A+

Avatar
Vincent Belaïche
Voir mon courrier précédent, et désolé pour la confusion, en fait je
faisais référence à un bout de code que j'avais vu et qui ``spécifiait''
(entre guillemets, vois le code et question et tu verras pourquoi) le
type sous-jascent de l'enum.

Vincent B.


Samuel DEVULDER a écrit :
Vincent Belaïche a écrit :

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.



En corolaire: Comme les énums sont a priori des valeurs signées (vu que
je peux avoir un énum qui vaut -1 par ex), est-ce que le compilo qui
accepterait le ":bits" grognerait si on a oublié d'y mettre un bit
supplémentaire pour les valeurs négatives?

Truc curieux avec le compilo C de HP sur les enums. Si on compile en
mode 32bit, alors ils sont signés. En 64bits il ne le sont pas, sauf si
on ajoute l'option "+se".

Du coup, si les enums sont des "int" avec choix libre laissé au compilo
du signe, peut on pour autant faire de l'arithmétique simple avec eux
sans risquer des soucis?

typedef enum {
LUNDI, MARDI, MERCREDI, JEUDI, VENDREDI, SAMEDI, DIMANCHE
} t_jour;

/* retourne vrai si j1 et j2 sont au plus séparés de 3 jours consécutifs */
int fenetre_3_jours(t_jour j1, t_jour j2) {
int delta = j1 - j2;
return -3<delta && delta<3;
}

Que donne fenetre_3_jours(LUNDI, MARDI) compilé sur machine HP 32bits ou
64bits?

Si on s'interdit la soustraction des enums, l'écriture de
fenetre_3_jours sera plus laborieuse (et peut être bugée):

return (j1==LUNDI && (j2<=MERCREDI || j2>=SAMEDI)) ||
(j1==MARDI && (j2<=JEUDI || j2==DIMANCHE)) ||
(j1==MERCREDI && (j2<=VENDREDI)) ||
(j1==JEUDI && (j2>=MARDI && j2<=SAMEDI)) ||
(j1==VENDREDI && (j2>=MERCREDI)) ||
(j1==SAMEDI && (j2>=JEUDI || j2==LUNDI) ||
(j1==DIMANCHE && (j2>=VENDREDI || j2<=MARDI);

sam.
Avatar
Samuel DEVULDER
Samuel DEVULDER a écrit :

/* retourne vrai si j1 et j2 sont au plus séparés de 3 jours consécutifs */
int fenetre_3_jours(t_jour j1, t_jour j2) {
int delta = j1 - j2;
return -3<delta && delta<3;
}



Nota: ce code ne fonctionne pas avec une artihmétique modulaire, c'est à
dire que pour lui fenetre_3_jours(DIMANCHE, LUNDI) renvoie faux.

Quelque part ca serait parfois utile si l'arithmétique sur les enums
était modulaire: on aurait DIMANCHE-LUNDI==6==-1 et donc compris entre
-2..2 (avec -2==7-2 mod 7). Ca donne une idée:

int delta = j1-j2;
if(delta<0) delta += 7;
return delta<=2 || delta>=(7-2);


sam.
Avatar
Samuel DEVULDER
Vincent Belaïche a écrit :
Au temps pour moi,

en effet, maintenant ça me revient, il s'agissait de spécifier le type
sousjacent et non d'un spécificateur en nombre de bits, mais c'était
fait sans C++0X de la façon suivante (attention j'ignore si ce code est
correct ou non, c'est qq chose que j'ai vu faire):

------------------------
enum couleurs {
rouge, vert, bleu
};
typedef char couleurs;
------------------------

Par la suite on utilise couleurs comme le nom de type (et nom `enum
couleurs')

Ça compile sans aucun conflit de nom puisque dans la première
déclaration couleurs n'est pas un nom de type.

J'aimerais savoir si un tel code est portable et à l'épreuve du temps.



Oui je les namespaces des vars/label/enums/struct/union sont tous
séparés ("struct toto toto;" est licite). Pour les typedefs ils sont vu
comme un "storage" (static, extern etc).. donc font parti des ordinary
identifiers tout comme les variables. Donc il n'y a pas de risque, je
pense que tu peux utiliser couleurs et struct couleurs simultanément, et
cela que couleurs représente une variable ou un typedef.

sam.
Avatar
Jean-Marc Bourguet
Vincent Belaïche writes:

Au temps pour moi,

en effet, maintenant ça me revient, il s'agissait de spécifier le type
sousjacent et non d'un spécificateur en nombre de bits, mais c'était fait
sans C++0X de la façon suivante (attention j'ignore si ce code est correct
ou non, c'est qq chose que j'ai vu faire):

------------------------
enum couleurs {
rouge, vert, bleu
};
typedef char couleurs;
------------------------

Par la suite on utilise couleurs comme le nom de type (et nom `enum
couleurs')

Ça compile sans aucun conflit de nom puisque dans la première déclaration
couleurs n'est pas un nom de type.

J'aimerais savoir si un tel code est portable et à l'épreuve du temps.



Il y a trois classes de noms en C:
- pour les tags (enum, union, struct)
- pour les labels
- pour les autres choses
et donc dans une portée donnée un identificateur peut désigner quelque
chose pour chacune des classes.

C'est le cas dans ton exemple, tu donnes un sens en tant que tag et un en
tant que typedef (ce dernier empéchant l'utilisation en tant que nom de
variable ou de fonction).

Ton code est bien défini en C et je ne vois pas pourquoi ça changerait.
J'aurais cependant tendance à l'éviter et à utiliser un nom de tag
différent de celui du typedef.

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