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.
xxxxx cinq (six avec cpp) - pour les macros (et constantes manifestes) du préprocesseur, qui de plus écrasent tout sur leur passage
- pour les tags (enum, union, struct) - pour les labels - pour les autres choses
- pour les membres d'une déclaration structurée (enum, etc.) - pour les noms des paramètres dans les déclarations de prototypes
et donc dans une portée donnée un identificateur peut désigner quelque chose pour chacune des classes.
(sauf les symboles du préprocesseur)
Ce qui ne change rien à la pertinence du commentaire de Jean-Marc.
Antoine
Jean-Marc Bourguet
Antoine Leca writes:
Jean-Marc Bourguet écrivit :
Il y a trois classes de noms en C:
xxxxx cinq (six avec cpp) - pour les macros (et constantes manifestes) du préprocesseur, qui de plus écrasent tout sur leur passage
OK mais le préprocesseur est tellement à part.
- pour les tags (enum, union, struct) - pour les labels - pour les autres choses
- pour les membres d'une déclaration structurée (enum, etc.) - pour les noms des paramètres dans les déclarations de prototypes
Plus délicat. J'ai tendance à considérer ces contextes comme des portées (scope en anglais). (Je n'ai pas le temps d'aller voir comment c'est fait formellement en C; je suis quasiment sûr que ce sont des portées pour le C++. Et commencer à parler de cela mais fait me poser des questions au sujet des tags...)
A+
-- Jean-Marc FAQ de fclc: http://www.levenez.com/lang/c/faq Site de usenet-fr: http://www.usenet-fr.news.eu.org
Antoine Leca <root@localhost.invalid> writes:
Jean-Marc Bourguet écrivit :
Il y a trois classes de noms en C:
xxxxx cinq (six avec cpp)
- pour les macros (et constantes manifestes) du préprocesseur,
qui de plus écrasent tout sur leur passage
OK mais le préprocesseur est tellement à part.
- pour les tags (enum, union, struct)
- pour les labels
- pour les autres choses
- pour les membres d'une déclaration structurée (enum, etc.)
- pour les noms des paramètres dans les déclarations de prototypes
Plus délicat. J'ai tendance à considérer ces contextes comme des portées
(scope en anglais). (Je n'ai pas le temps d'aller voir comment c'est fait
formellement en C; je suis quasiment sûr que ce sont des portées pour le
C++. Et commencer à parler de cela mais fait me poser des questions au
sujet des tags...)
A+
--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
xxxxx cinq (six avec cpp) - pour les macros (et constantes manifestes) du préprocesseur, qui de plus écrasent tout sur leur passage
OK mais le préprocesseur est tellement à part.
- pour les tags (enum, union, struct) - pour les labels - pour les autres choses
- pour les membres d'une déclaration structurée (enum, etc.) - pour les noms des paramètres dans les déclarations de prototypes
Plus délicat. J'ai tendance à considérer ces contextes comme des portées (scope en anglais). (Je n'ai pas le temps d'aller voir comment c'est fait formellement en C; je suis quasiment sûr que ce sont des portées pour le C++. Et commencer à parler de cela mais fait me poser des questions au sujet des tags...)
A+
-- Jean-Marc FAQ de fclc: http://www.levenez.com/lang/c/faq Site de usenet-fr: http://www.usenet-fr.news.eu.org
Lucas Levrel
Le 7 avril 2010, Samuel DEVULDER a écrit :
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):
Si je ne m'abuse la solution standard *et* lisible c'est int delta=(j1<j2 ? j2-j1 : j1-j2);
-- LL
Manuel Pégourié-Gonnard
Vincent Belaïche scripsit :
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):
Juste pour être sûr : ce code est valide en C, mais pas en C++, non ?
-- Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Vincent Belaïche scripsit :
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):
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):
Juste pour être sûr : ce code est valide en C, mais pas en C++, non ?
-- Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Antoine Leca
Jean-Marc Bourguet a écrit :
- pour les tags (enum, union, struct) - pour les labels - pour les autres choses
- pour les membres d'une déclaration structurée (enum, etc.) - pour les noms des paramètres dans les déclarations de prototypes
Plus délicat. J'ai tendance à considérer ces contextes comme des portées (scope en anglais).
La norme les pose au même niveau dans l'introduction sur la taxinomie (6.2.3). Et de fait, avec un seul lexème de contexte tu peux déterminer de quoi il s'agit : - avant un tag, tu auras struct, union ou enum, et vice-versa - un (sélecteur de) membre vient derrière . ou ->, et vice-versa - un label sera, ou après un goto, ou avant : (et les règles sont plus complexes, ce qui est cohérent avec le fait que la portée des labels soit elle aussi un concept parallèle aux autres portées) - et sinon c'est un tout-venant.
En ce qui concerne les noms de paramètres, c'est une bourde de ma part [rouge]. En fait il s'agit d'une portée différente, et au niveau syntaxique uniquement, il n'y a pas parallélisme comme pour les macros ou les labels.
Antoine
Jean-Marc Bourguet a écrit :
- pour les tags (enum, union, struct)
- pour les labels
- pour les autres choses
- pour les membres d'une déclaration structurée (enum, etc.)
- pour les noms des paramètres dans les déclarations de prototypes
Plus délicat. J'ai tendance à considérer ces contextes comme des portées
(scope en anglais).
La norme les pose au même niveau dans l'introduction sur la taxinomie
(6.2.3). Et de fait, avec un seul lexème de contexte tu peux déterminer
de quoi il s'agit :
- avant un tag, tu auras struct, union ou enum, et vice-versa
- un (sélecteur de) membre vient derrière . ou ->, et vice-versa
- un label sera, ou après un goto, ou avant : (et les règles sont plus
complexes, ce qui est cohérent avec le fait que la portée des labels
soit elle aussi un concept parallèle aux autres portées)
- et sinon c'est un tout-venant.
En ce qui concerne les noms de paramètres, c'est une bourde de ma part
[rouge]. En fait il s'agit d'une portée différente, et au niveau
syntaxique uniquement, il n'y a pas parallélisme comme pour les macros
ou les labels.
- pour les tags (enum, union, struct) - pour les labels - pour les autres choses
- pour les membres d'une déclaration structurée (enum, etc.) - pour les noms des paramètres dans les déclarations de prototypes
Plus délicat. J'ai tendance à considérer ces contextes comme des portées (scope en anglais).
La norme les pose au même niveau dans l'introduction sur la taxinomie (6.2.3). Et de fait, avec un seul lexème de contexte tu peux déterminer de quoi il s'agit : - avant un tag, tu auras struct, union ou enum, et vice-versa - un (sélecteur de) membre vient derrière . ou ->, et vice-versa - un label sera, ou après un goto, ou avant : (et les règles sont plus complexes, ce qui est cohérent avec le fait que la portée des labels soit elle aussi un concept parallèle aux autres portées) - et sinon c'est un tout-venant.
En ce qui concerne les noms de paramètres, c'est une bourde de ma part [rouge]. En fait il s'agit d'une portée différente, et au niveau syntaxique uniquement, il n'y a pas parallélisme comme pour les macros ou les labels.
Antoine
Antoine Leca
Lucas Levrel a écrit :
Le 7 avril 2010, Samuel DEVULDER a écrit :
Si on s'interdit la soustraction des enums,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Si je ne m'abuse la solution standard *et* lisible c'est int delta=(j1<j2 ? j2-j1 : j1-j2);
?
Antoine
Lucas Levrel a écrit :
Le 7 avril 2010, Samuel DEVULDER a écrit :
Si on s'interdit la soustraction des enums,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Si je ne m'abuse la solution standard *et* lisible c'est
int delta=(j1<j2 ? j2-j1 : j1-j2);
Si je ne m'abuse la solution standard *et* lisible c'est int delta=(j1<j2 ? j2-j1 : j1-j2);
?
Antoine
Jean-Marc Bourguet
Manuel Pégourié-Gonnard writes:
Vincent Belaïche scripsit :
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):
Juste pour être sûr : ce code est valide en C, mais pas en C++, non ?
Exact. En gros en C++ il y un typedef automatique
typedef struct/union/enum t t;
avec des exceptions pour autoriser
struct stat;
int stat();
mais la forme ci-dessus ne relève pas des exceptions.
A+
-- Jean-Marc FAQ de fclc: http://www.levenez.com/lang/c/faq Site de usenet-fr: http://www.usenet-fr.news.eu.org
Manuel Pégourié-Gonnard <mpg@elzevir.fr> writes:
Vincent Belaïche scripsit :
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):
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):
Juste pour être sûr : ce code est valide en C, mais pas en C++, non ?
Exact. En gros en C++ il y un typedef automatique
typedef struct/union/enum t t;
avec des exceptions pour autoriser
struct stat;
int stat();
mais la forme ci-dessus ne relève pas des exceptions.
A+
-- Jean-Marc FAQ de fclc: http://www.levenez.com/lang/c/faq Site de usenet-fr: http://www.usenet-fr.news.eu.org
Samuel DEVULDER
Lucas Levrel a écrit :
Le 7 avril 2010, Samuel DEVULDER a écrit :
int fenetre_3_jours(t_jour j1, t_jour j2) { int delta = j1 - j2; return -3<delta && delta<3; }
Si je ne m'abuse la solution standard *et* lisible c'est int delta=(j1<j2 ? j2-j1 : j1-j2);
avec: return delta<3 || delta>4; (pour une semaine modulo 7)
Oui, il faut faire comme tu l'indiques si on veut éviter le débordement de signe (et son UB je suppose) en cas d'unsigned. C'est le pb du absdiff() dont faisait référence Marc Espie dans "Re: Implementation strlen()" je crois.
Mais qu'est ce qui dit que l'enum est unsigned? Mystère!
On peut être du coup tenté de dire que la bonne façon d'utiliser les enums, c'est de dire que ce ne sont pas des nombre qu'on peut soustraire, additionner, (multiplier?) avec une arithmétique (et des soucis signed/unsigned pas clairs), mais uniquement des "symboles", qu'on peut ordonner avec "<" ou "==".
En meme temps l'ordre avec "<" se discute car il ne reflète pas celui de la déclaration (quid si on avait écrit MERCREDI=-15 en plein milieu?) Par contre "==" me semble marcher à tous les coups, mais produit un truc assez moche qui revient à énumérer tous les couples de jours.
Pour éviter tout cela il faudrait en fait écrire une fonction de conversion entre enum->int, qui respecte le "<" malgrès la présence d'un MERCREDI=-15, et qui soit signed pour pouvoir calculer la distance entre 2 jours facilement:
int jour2int(t_jour j) { int n = -1; switch(j) { case DIMANCHE: ++n; /* fall through */ case SAMEDI: ++n; /* fall through */ case VENDREDI: ++n; /* fall through */ case JEUDI: ++n; /* fall through */ case MERCREDI: ++n; /* fall through */ case MARDI: ++n; /* fall through */ case LUNDI: ++n; /* fall through */ } return n; } (nota: ici le fall through, c'est juste pour varier les plaisirs. On aurait pu banalement écrire return 2, 3 ou 4.)
Résultat = abs(jour2int(j1) - jour2int(j2))<3;
sam.
Lucas Levrel a écrit :
Le 7 avril 2010, Samuel DEVULDER a écrit :
int fenetre_3_jours(t_jour j1, t_jour j2) {
int delta = j1 - j2;
return -3<delta && delta<3;
}
Si je ne m'abuse la solution standard *et* lisible c'est
int delta=(j1<j2 ? j2-j1 : j1-j2);
avec:
return delta<3 || delta>4;
(pour une semaine modulo 7)
Oui, il faut faire comme tu l'indiques si on veut éviter le débordement
de signe (et son UB je suppose) en cas d'unsigned. C'est le pb du
absdiff() dont faisait référence Marc Espie dans "Re: Implementation
strlen()" je crois.
Mais qu'est ce qui dit que l'enum est unsigned? Mystère!
On peut être du coup tenté de dire que la bonne façon d'utiliser les
enums, c'est de dire que ce ne sont pas des nombre qu'on peut
soustraire, additionner, (multiplier?) avec une arithmétique (et des
soucis signed/unsigned pas clairs), mais uniquement des "symboles",
qu'on peut ordonner avec "<" ou "==".
En meme temps l'ordre avec "<" se discute car il ne reflète pas celui de
la déclaration (quid si on avait écrit MERCREDI=-15 en plein milieu?)
Par contre "==" me semble marcher à tous les coups, mais produit un truc
assez moche qui revient à énumérer tous les couples de jours.
Pour éviter tout cela il faudrait en fait écrire une fonction de
conversion entre enum->int, qui respecte le "<" malgrès la présence d'un
MERCREDI=-15, et qui soit signed pour pouvoir calculer la distance entre
2 jours facilement:
int jour2int(t_jour j) {
int n = -1;
switch(j) {
case DIMANCHE: ++n; /* fall through */
case SAMEDI: ++n; /* fall through */
case VENDREDI: ++n; /* fall through */
case JEUDI: ++n; /* fall through */
case MERCREDI: ++n; /* fall through */
case MARDI: ++n; /* fall through */
case LUNDI: ++n; /* fall through */
}
return n;
}
(nota: ici le fall through, c'est juste pour varier les plaisirs. On
aurait pu banalement écrire return 2, 3 ou 4.)
int fenetre_3_jours(t_jour j1, t_jour j2) { int delta = j1 - j2; return -3<delta && delta<3; }
Si je ne m'abuse la solution standard *et* lisible c'est int delta=(j1<j2 ? j2-j1 : j1-j2);
avec: return delta<3 || delta>4; (pour une semaine modulo 7)
Oui, il faut faire comme tu l'indiques si on veut éviter le débordement de signe (et son UB je suppose) en cas d'unsigned. C'est le pb du absdiff() dont faisait référence Marc Espie dans "Re: Implementation strlen()" je crois.
Mais qu'est ce qui dit que l'enum est unsigned? Mystère!
On peut être du coup tenté de dire que la bonne façon d'utiliser les enums, c'est de dire que ce ne sont pas des nombre qu'on peut soustraire, additionner, (multiplier?) avec une arithmétique (et des soucis signed/unsigned pas clairs), mais uniquement des "symboles", qu'on peut ordonner avec "<" ou "==".
En meme temps l'ordre avec "<" se discute car il ne reflète pas celui de la déclaration (quid si on avait écrit MERCREDI=-15 en plein milieu?) Par contre "==" me semble marcher à tous les coups, mais produit un truc assez moche qui revient à énumérer tous les couples de jours.
Pour éviter tout cela il faudrait en fait écrire une fonction de conversion entre enum->int, qui respecte le "<" malgrès la présence d'un MERCREDI=-15, et qui soit signed pour pouvoir calculer la distance entre 2 jours facilement:
int jour2int(t_jour j) { int n = -1; switch(j) { case DIMANCHE: ++n; /* fall through */ case SAMEDI: ++n; /* fall through */ case VENDREDI: ++n; /* fall through */ case JEUDI: ++n; /* fall through */ case MERCREDI: ++n; /* fall through */ case MARDI: ++n; /* fall through */ case LUNDI: ++n; /* fall through */ } return n; } (nota: ici le fall through, c'est juste pour varier les plaisirs. On aurait pu banalement écrire return 2, 3 ou 4.)
Résultat = abs(jour2int(j1) - jour2int(j2))<3;
sam.
Jean-Marc Bourguet
Antoine Leca writes:
Jean-Marc Bourguet a écrit :
- pour les tags (enum, union, struct) - pour les labels - pour les autres choses
- pour les membres d'une déclaration structurée (enum, etc.)
Plus délicat. J'ai tendance à considérer ces contextes comme des portées (scope en anglais).
La norme les pose au même niveau dans l'introduction sur la taxinomie (6.2.3). Et de fait, avec un seul lexème de contexte tu peux déterminer de quoi il s'agit : - avant un tag, tu auras struct, union ou enum, et vice-versa - un (sélecteur de) membre vient derrière . ou ->, et vice-versa - un label sera, ou après un goto, ou avant : (et les règles sont plus complexes, ce qui est cohérent avec le fait que la portée des labels soit elle aussi un concept parallèle aux autres portées) - et sinon c'est un tout-venant.
Merci pour la recherche.
A+
-- Jean-Marc FAQ de fclc: http://www.levenez.com/lang/c/faq Site de usenet-fr: http://www.usenet-fr.news.eu.org
Antoine Leca <root@localhost.invalid> writes:
Jean-Marc Bourguet a écrit :
- pour les tags (enum, union, struct)
- pour les labels
- pour les autres choses
- pour les membres d'une déclaration structurée (enum, etc.)
Plus délicat. J'ai tendance à considérer ces contextes comme des portées
(scope en anglais).
La norme les pose au même niveau dans l'introduction sur la taxinomie
(6.2.3). Et de fait, avec un seul lexème de contexte tu peux déterminer
de quoi il s'agit :
- avant un tag, tu auras struct, union ou enum, et vice-versa
- un (sélecteur de) membre vient derrière . ou ->, et vice-versa
- un label sera, ou après un goto, ou avant : (et les règles sont plus
complexes, ce qui est cohérent avec le fait que la portée des labels
soit elle aussi un concept parallèle aux autres portées)
- et sinon c'est un tout-venant.
Merci pour la recherche.
A+
--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
- pour les tags (enum, union, struct) - pour les labels - pour les autres choses
- pour les membres d'une déclaration structurée (enum, etc.)
Plus délicat. J'ai tendance à considérer ces contextes comme des portées (scope en anglais).
La norme les pose au même niveau dans l'introduction sur la taxinomie (6.2.3). Et de fait, avec un seul lexème de contexte tu peux déterminer de quoi il s'agit : - avant un tag, tu auras struct, union ou enum, et vice-versa - un (sélecteur de) membre vient derrière . ou ->, et vice-versa - un label sera, ou après un goto, ou avant : (et les règles sont plus complexes, ce qui est cohérent avec le fait que la portée des labels soit elle aussi un concept parallèle aux autres portées) - et sinon c'est un tout-venant.
Merci pour la recherche.
A+
-- Jean-Marc FAQ de fclc: http://www.levenez.com/lang/c/faq Site de usenet-fr: http://www.usenet-fr.news.eu.org
Antoine Leca
Samuel DEVULDER écrivit :
Mais qu'est ce qui dit que l'enum est unsigned? Mystère!
Si tu fabriques des membres d'une énumération qui ont des valeurs plus grandes que INT_MAX (32767 en 16 bits, cela va vite), de toutes façons tu es dans le royaume des extensions propriétaires.
Maintenant, si ton implémentation décide de son propre chef de rendre non signé (retypé en unsigned voire unsigned long) une constante d'énumération alors même que sa valeur est inférieure à INT_MAX, c'est une non-conformité en bonne et due forme.
Antoine
Samuel DEVULDER écrivit :
Mais qu'est ce qui dit que l'enum est unsigned? Mystère!
Si tu fabriques des membres d'une énumération qui ont des valeurs plus
grandes que INT_MAX (32767 en 16 bits, cela va vite), de toutes façons
tu es dans le royaume des extensions propriétaires.
Maintenant, si ton implémentation décide de son propre chef de rendre
non signé (retypé en unsigned voire unsigned long) une constante
d'énumération alors même que sa valeur est inférieure à INT_MAX, c'est
une non-conformité en bonne et due forme.
Mais qu'est ce qui dit que l'enum est unsigned? Mystère!
Si tu fabriques des membres d'une énumération qui ont des valeurs plus grandes que INT_MAX (32767 en 16 bits, cela va vite), de toutes façons tu es dans le royaume des extensions propriétaires.
Maintenant, si ton implémentation décide de son propre chef de rendre non signé (retypé en unsigned voire unsigned long) une constante d'énumération alors même que sa valeur est inférieure à INT_MAX, c'est une non-conformité en bonne et due forme.