web-dev-qa-db-fra.com

Différence entre les énoncés Enum et Define

Quelle est la différence entre l'utilisation d'une instruction define et d'une instruction enum en C/C++ (et y a-t-il une différence en les utilisant avec C ou C++)?

Par exemple, quand faut-il utiliser

enum {BUFFER = 1234}; 

plus de

#define BUFFER 1234   
45
Zain Rizvi

enum définit un élément syntaxique.

#define est une directive pré-pré-processeur, exécutée avant le compilateur voit le code et n'est donc pas un élément de langage de C lui-même.

Généralement, les énumérations sont préférées car elles sont sécuritaires et plus faciles à détecter. Les définitions sont plus difficiles à localiser et peuvent avoir un comportement complexe. Par exemple, un morceau de code peut redéfinir un #define créé par un autre. Cela peut être difficile à localiser.

56
Jason Cohen

Les instructions #define sont gérées par le pré-processeur avant que le compilateur ne voie le code. Il s’agit donc d’une substitution de texte (c’est en fait un peu plus intelligent avec l’utilisation de paramètres, par exemple).

Les énumérations font partie du langage C lui-même et présentent les avantages suivants.

1/Ils peuvent avoir un type et le compilateur peut les vérifier.

2/Puisqu'ils sont disponibles pour le compilateur, les informations de symbole qui les concernent peuvent être transmises au débogueur, ce qui facilite le débogage.

16
paxdiablo

Define est une commande de préprocesseur, comme si vous remplaciez tout dans votre éditeur: vous pouvez remplacer une chaîne par une autre, puis compiler le résultat.

Enum est un cas particulier de type, par exemple, si vous écrivez:

enum ERROR_TYPES
{
   REGULAR_ERR =1,
   OK =0
}

il existe un nouveau type appelé ERROR_TYPES . Il est vrai que REGULAR_ERR donne la valeur 1, mais la diffusion de ce type vers int devrait produire un avertissement de transtypage (si vous configurez votre compilateur sur une verbosité élevée).

Résumé: Ils sont identiques, mais lorsque vous utilisez enum, vous bénéficiez de la vérification de type et, en définissant, vous remplacez simplement les chaînes de code.

8
akiva

Les énumérations sont généralement préférées à #define partout où il est judicieux d’utiliser une énumération:

  • Les débogueurs peuvent vous montrer le nom symbolique d'une valeur enums ("openType: OpenExisting", plutôt que "openType: 2"
  • Vous bénéficiez d'un peu plus de protection contre les conflits de noms, mais ce n'est pas aussi grave que c'était (la plupart des compilateurs mettent en garde sur re#defineition.

La plus grande différence est que vous pouvez utiliser des énumérations en tant que types:

// Yeah, dumb example
enum OpenType {
    OpenExisting,
    OpenOrCreate,
    Truncate
};

void OpenFile(const char* filename, OpenType openType, int bufferSize);

Cela vous permet de vérifier le type des paramètres (vous ne pouvez pas mélanger openType et bufferSize), et facilite la recherche des valeurs valides, facilitant ainsi l’utilisation de vos interfaces. Certains IDE peuvent même vous donner intellisense complétion de code!

5
Simon Buchan

Il est toujours préférable d'utiliser un enum si possible. L'utilisation d'une énumération donne au compilateur plus d'informations sur votre code source. Un compilateur préprocesseur défini n'est jamais vu par le compilateur et transporte donc moins d'informations.

Pour la mise en œuvre, par exemple un tas de modes, en utilisant une énumération, permet au compilateur d'attraper les instructions case- manquantes dans un commutateur, par exemple.

4
unwind

enum peut regrouper plusieurs éléments dans une catégorie:

enum fruits{ Apple=1234, orange=12345};

tandis que #define ne peut créer que des constantes non liées:

#define Apple 1234
#define orange 12345
3
Andy

#define est une commande de préprocesseur, enum est en langage C ou C++.

Il est toujours préférable d’utiliser des énumérations sur #define pour ce type de cas. Une chose est la sécurité de type. En outre, lorsque vous avez une séquence de valeurs, il vous suffit de donner le début de la séquence dans l’énumération, les autres valeurs reçoivent des valeurs consécutives.

enum {
  ONE = 1,
  TWO,
  THREE,
  FOUR
};

au lieu de

#define ONE 1
#define TWO 2
#define THREE 3
#define FOUR 4

En passant, il reste encore des cas où vous devrez peut-être utiliser #define (généralement pour certaines sortes de macros, si vous devez être capable de construire un identifiant contenant la constante), mais c'est une sorte de macro Black Magic. , et très très rare pour être le chemin à parcourir. Si vous allez à ces extrémités, vous devriez probablement utiliser un modèle C++ (mais si vous êtes coincé avec C ...).

3
kriss

Outre tout ce qui est déjà écrit, on dit mais pas montré et c'est plutôt intéressant. Par exemple.

enum action { DO_JUMP, DO_TURNL, DO_TURNR, DO_STOP };
//...
void do_action( enum action anAction, info_t x );

Considérer l'action comme un type rend la chose plus claire. En utilisant define, vous auriez écrit

void do_action(int anAction, info_t x);
2
ShinTakezou

Pour les valeurs constantes intégrales, je préfère enum à #define. L'utilisation de enum (ne minimisant que l'inconvénient d'un peu plus de frappe) ne semble présenter aucun inconvénient, mais vous avez l'avantage que enum peut être défini, alors que les identificateurs #define ont une portée globale qui affecte tout.

Utiliser #define n'est généralement pas un problème, mais comme il n'y a pas d'inconvénient à enum, j'y vais.

En C++, je préfère également généralement enum à const int même si en C++, un const int peut remplacer un entier littéral (contrairement à C) car enum est portable en C (dans lequel je travaille toujours beaucoup).

2
Michael Burr

Si vous ne voulez que cette constante unique (par exemple pour buffersize), je n’utiliserais pas une énumération, mais une définition. J'utiliserais des énumérations pour des choses comme les valeurs de retour (qui signifient différentes conditions d'erreur) et chaque fois que nous devons distinguer différents "types" ou "cas". Dans ce cas, nous pouvons utiliser une énumération pour créer un nouveau type utilisable dans les prototypes de fonctions, etc., puis le compilateur pourra vérifier ce code de manière plus efficace.

2
Henno Brandsma

Un autre avantage d'une énumération sur une liste de définitions est que les compilateurs (au moins gcc) peuvent générer un avertissement lorsque toutes les valeurs ne sont pas vérifiées dans une instruction switch. Par exemple:

enum {
    STATE_ONE,
    STATE_TWO,
    STATE_THREE
};

...

switch (state) {
case STATE_ONE:
    handle_state_one();
    break;
case STATE_TWO:
    handle_state_two();
    break;
};

Dans le code précédent, le compilateur est capable de générer un avertissement indiquant que toutes les valeurs de l'énum ne sont pas gérées dans le commutateur. Si les états étaient faits comme # define, ce ne serait pas le cas.

1
Russell Bryant

Si vous avez un groupe de constantes (comme "Jours de la semaine"), les énumérations sont préférables car elles indiquent qu'elles sont groupées. et, comme l'a dit Jason, ils sont dignes de confiance. Si c'est une constante globale (comme le numéro de version), c'est plutôt ce que vous utiliseriez un #define pour; bien que cela fasse l’objet de nombreux débats.

1
Smashery

La création d'une énumération crée non seulement des littéraux mais également le type qui les regroupe: Cela ajoute une sémantique à votre code que le compilateur est capable de vérifier.

De plus, lorsque vous utilisez un débogueur, vous avez accès aux valeurs des littéraux enum. Ce n'est pas toujours le cas avec #define.

1
mouviciel

Outre les avantages énumérés ci-dessus, vous pouvez limiter la portée des énumérations à une classe, une structure ou un espace de noms. Personnellement, j'aime avoir à la fois le nombre minimum de symboles pertinents dans la portée, ce qui est une autre raison d'utiliser des énumérations plutôt que #defines.

1
SmacL

Enum:

1. Généralement utilisé pour plusieurs valeurs

2. En enum il y a deux choses, l'une est nom et l'autre valeur, nom valeur doit être distinguée 1, et ainsi de suite, sauf indication explicite de la valeur.

3. Ils peuvent avoir un type et le compilateur peut les vérifier

4. Faciliter le débogage

5. Nous pouvons en limiter la portée à une classe.

Définir:

1. Quand nous devons définir une seule valeur 

2. Il remplace généralement une chaîne par une autre.

3. Sa portée est globale, nous ne pouvons pas en limiter la portée 

Globalement, nous devons utiliser enum 

1
user8194964

les énumérations sont plus utilisées pour énumérer une sorte d’ensemble, comme des jours dans une semaine. Si vous avez besoin d'un seul nombre constant, const int (ou double, etc.) sera définitivement meilleur qu'énum. Personnellement, je n'aime pas #define (du moins pas pour la définition de certaines constantes) car cela ne me donne pas la sécurité du type, mais vous pouvez bien sûr l'utiliser si cela vous convient mieux.

1
PeterK

Bien que plusieurs des réponses ci-dessus recommandent d'utiliser enum pour différentes raisons, je voudrais souligner que l'utilisation de définit présente un avantage réel lors du développement d'interfaces. Vous pouvez introduire de nouvelles options et laisser le logiciel les utiliser de manière conditionnelle.

Par exemple:


 #define OPT_X1 1/* introduit dans la version 1 */
 #define OPT_X2 2/* introduit dans la version 2 */

Ensuite, un logiciel qui peut être compilé avec l'une ou l'autre version qu'il peut faire


 #ifdef OPT_X2 
 int flags = OPT_X2; 
 #autre
 int flags = 0; 
 #fin si

Lors d'une énumération, cela n'est pas possible sans un mécanisme de détection de fonctionnalité au moment de l'exécution.

0
Nikos

Il y a peu de différence. La norme C stipule que les énumérations ont un type intégral et que les constantes d’énumération sont du type int. Elles peuvent donc être mélangées librement avec d’autres types intégraux, sans erreur. (Si, en revanche, un tel mélange est interdit sans un transfert explicite, une utilisation judicieuse des énumérations peut intercepter certaines erreurs de programmation.)

Certains des avantages des énumérations sont que les valeurs numériques sont automatiquement attribuées, qu'un débogueur peut être capable d'afficher les valeurs symboliques lorsque les variables d'énumération sont examinées et qu'elles obéissent à la portée du bloc. (Un compilateur peut également générer des avertissements non mortels lorsque les énumérations sont mélangées sans distinction, car cela peut toujours être considéré comme un mauvais style même s'il n'est pas strictement illégal.) Un inconvénient est que le programmeur a peu de contrôle sur ces avertissements non mortels; certains programmeurs ont aussi regretté de ne pas avoir le contrôle de la taille des variables de dénombrement. 

0
x0v