“Statique const” vs “#define” vs “enum”

Lequel est préférable d’utiliser parmi les énoncés ci-dessous en C?

static const int var = 5; 

ou

 #define var 5 

ou

 enum { var = 5 }; 

En général:

 static const 

Parce qu’il respecte la scope et est de type sécurisé.

La seule mise en garde que je pourrais voir: si vous souhaitez que la variable soit éventuellement définie sur la ligne de commande. Il y a encore une alternative:

 #ifdef VAR // Very bad name, not long enough, too general, etc.. static int const var = VAR; #else static int const var = 5; // default value #endif 

Dans la mesure du possible, au lieu de macros / points de suspension, utilisez une alternative sécurisée.

Si vous avez vraiment besoin d’aller avec une macro (par exemple, vous voulez __FILE__ ou __LINE__ ), vous devriez alors nommer TRÈS soigneusement votre macro: dans sa convention de dénomination, Boost recommande toutes les majuscules, commençant par le nom du projet ( ici BOOST_), en parcourant la bibliothèque, vous remarquerez que c’est (généralement) suivi du nom de la zone particulière (bibliothèque) puis d’un nom significatif.

Cela donne généralement de longs noms 🙂

Cela dépend de ce dont vous avez besoin de la valeur. Vous (et tous les autres jusqu’à présent) avez omis la troisième alternative:

  1. static const int var = 5;
  2. #define var 5
  3. enum { var = 5 };

Ignorer les questions concernant le choix du nom, alors:

  • Si vous avez besoin de passer un pointeur, vous devez utiliser (1).
  • Puisque (2) est apparemment une option, vous n’avez pas besoin de faire passer des pointeurs.
  • Les deux (1) et (3) ont un symbole dans la table des symboles du débogueur, ce qui facilite le débogage. Il est plus probable que (2) n’aura pas de symbole, vous laissant vous demander ce que c’est.
  • (1) ne peut pas être utilisé comme dimension pour les tableaux à scope mondiale; les deux (2) et (3) peuvent.
  • (1) ne peut pas être utilisé comme dimension pour les tableaux statiques au niveau de la fonction; les deux (2) et (3) peuvent.
  • Sous C99, tous ces éléments peuvent être utilisés pour les tableaux locaux. Techniquement, l’utilisation de (1) impliquerait l’utilisation d’un VLA (tableau de longueur variable), bien que la dimension référencée par «var» soit bien sûr fixée à la taille 5.
  • (1) ne peut pas être utilisé dans des endroits tels que les instructions de commutation; les deux (2) et (3) peuvent.
  • (1) ne peut pas être utilisé pour initialiser des variables statiques; les deux (2) et (3) peuvent.
  • (2) peut changer le code que vous ne souhaitez pas modifier car il est utilisé par le préprocesseur; les deux (1) et (3) n’auront pas d’effets secondaires inattendus comme celui-là.
  • Vous pouvez détecter si (2) a été défini dans le préprocesseur; ni (1) ni (3) ne le permettent.

Donc, dans la plupart des contextes, préférez le «enum» aux alternatives. Sinon, les premier et dernier points sont probablement les facteurs déterminants – et vous devez réfléchir davantage si vous devez satisfaire les deux à la fois.

Si vous parliez de C ++, vous utiliseriez alors l’option (1) – la constante statique – à chaque fois.

En C, spécifiquement? En C, la réponse correcte est: utilisez #define (ou, le cas échéant, enum )

Bien qu’il soit avantageux d’avoir les propriétés de cadrage et de typage d’un object const , en réalité les objects const en C (par opposition à C ++) ne sont pas de véritables constantes et sont donc généralement inutiles dans la plupart des cas.

Donc, en C, le choix doit être déterminé par la manière dont vous prévoyez d’utiliser votre constante. Par exemple, vous ne pouvez pas utiliser un object const int comme une étiquette de case (lorsqu’une macro fonctionnera). Vous ne pouvez pas utiliser un object const int comme une largeur de champ binary (pendant qu’une macro fonctionnera). En C89 / 90, vous ne pouvez pas utiliser un object const pour spécifier une taille de tableau (lorsqu’une macro fonctionnera). Même en C99, vous ne pouvez pas utiliser un object const pour spécifier une taille de tableau lorsque vous avez besoin d’un tableau autre que VLA .

Si cela est important pour vous, cela déterminera votre choix. La plupart du temps, vous n’aurez pas d’autre choix que d’utiliser #define in C. Et n’oubliez pas une autre alternative, qui produit de vraies constantes dans C- enum .

En C ++, les objects const sont de vraies constantes, donc en C ++, il est presque toujours préférable de préférer la variante const (pas besoin de static explicite en C ++).

La différence entre static const #define et #define est que le premier utilise la mémoire et que le dernier n’utilise pas la mémoire pour le stockage. Deuxièmement, vous ne pouvez pas passer l’adresse d’un #define alors que vous pouvez passer l’adresse d’un static const . En fait, cela dépend de la situation dans laquelle nous nous trouvons, nous devons en choisir un parmi ces deux. Les deux sont à leur meilleur dans différentes circonstances. S’il vous plaît ne supposez pas que l’un est meilleur que l’autre … 🙂

Si cela avait été le cas, Dennis Ritchie aurait gardé le meilleur seul … hahaha … 🙂

En C #define est beaucoup plus populaire. Vous pouvez utiliser ces valeurs pour déclarer des tailles de tableau, par exemple:

 #define MAXLEN 5 void foo(void) { int bar[MAXLEN]; } 

ANSI C ne vous permet pas d’utiliser static const dans ce contexte à ma connaissance. En C ++, vous devez éviter les macros dans ces cas. Tu peux écrire

 const int maxlen = 5; void foo() { int bar[maxlen]; } 

et même omettre static car le lien interne est impliqué par const déjà [en C ++ seulement].

Un autre inconvénient de const en C est que vous ne pouvez pas utiliser la valeur pour initialiser un autre const .

 static int const NUMBER_OF_FINGERS_PER_HAND = 5; static int const NUMBER_OF_HANDS = 2; // initializer element is not constant, this does not work. static int const NUMBER_OF_FINGERS = NUMBER_OF_FINGERS_PER_HAND * NUMBER_OF_HANDS; 

Même cela ne fonctionne pas avec un const puisque le compilateur ne le voit pas comme une constante:

 static uint8_t const ARRAY_SIZE = 16; static int8_t const lookup_table[ARRAY_SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // ARRAY_SIZE not a constant! 

Je serais ravi d’utiliser const typé dans ces cas, sinon …

Si vous parvenez à vous en sortir, static const a beaucoup d’avantages. Elle obéit aux principes de la scope normale, est visible dans un débogueur et obéit généralement aux règles auxquelles les variables obéissent.

Cependant, au moins dans la norme C d’origine, ce n’est pas une constante. Si vous utilisez #define var 5 , vous pouvez écrire int foo[var]; comme une déclaration, mais vous ne pouvez pas le faire (sauf en tant qu’extension du compilateur “avec static const int var = 5; Ce n’est pas le cas en C ++, où la version static const peut être utilisée partout où la version #define peut, et je crois que c’est aussi le cas avec C99.

Cependant, ne nommez jamais une constante #define avec un nom en minuscule. Il remplacera toute utilisation possible de ce nom jusqu’à la fin de l’unité de traduction. Les constantes macro doivent être dans ce qui est effectivement leur propre espace de noms, qui est traditionnellement constitué de toutes les majuscules, peut-être avec un préfixe.

#define var 5 vous causera des problèmes si vous avez des choses comme mystruct.var .

Par exemple,

 struct mystruct { int var; }; #define var 5 int main() { struct mystruct foo; foo.var = 1; return 0; } 

Le préprocesseur le remplacera et le code ne sera pas compilé. Pour cette raison, le style de codage traditionnel suggère que tous les #define constants utilisent des majuscules pour éviter les conflits.

J’ai écrit un programme de test rapide pour démontrer une différence:

 #include  enum {ENUM_DEFINED=16}; enum {ENUM_DEFINED=32}; #define DEFINED_DEFINED 16 #define DEFINED_DEFINED 32 int main(int argc, char *argv[]) { printf("%d, %d\n", DEFINED_DEFINED, ENUM_DEFINED); return(0); } 

Cela comstack avec ces erreurs et avertissements:

 main.c:6:7: error: redefinition of enumerator 'ENUM_DEFINED' enum {ENUM_DEFINED=32}; ^ main.c:5:7: note: previous definition is here enum {ENUM_DEFINED=16}; ^ main.c:9:9: warning: 'DEFINED_DEFINED' macro redefined [-Wmacro-redefined] #define DEFINED_DEFINED 32 ^ main.c:8:9: note: previous definition is here #define DEFINED_DEFINED 16 ^ 

Notez que enum donne une erreur lorsque define donne un avertissement.

La définition

 const int const_value = 5; 

ne définit pas toujours une valeur constante. Certains compilateurs (par exemple tcc 0.9.26 ) allouent simplement de la mémoire identifiée par le nom “const_value”. En utilisant l’identifiant “const_value”, vous ne pouvez pas modifier cette mémoire. Mais vous pouvez toujours modifier la mémoire en utilisant un autre identifiant:

 const int const_value = 5; int *mutable_value = (int*) &const_value; *mutable_value = 3; printf("%i", const_value); // The output may be 5 or 3, depending on the comstackr. 

Cela signifie la définition

 #define CONST_VALUE 5 

est le seul moyen de définir une valeur constante qui ne peut être modifiée par aucun moyen.

Il est TOUJOURS préférable d’utiliser const, au lieu de #define. C’est parce que const est traité par le compilateur et #define par le préprocesseur. C’est comme si #define lui-même ne faisait pas partie du code (grosso modo).

Exemple:

 #define PI 3.1416 

Le nom symbolique PI ne peut jamais être vu par les compilateurs; il peut être supprimé par le préprocesseur avant que le code source ne parvienne même à un compilateur. Par conséquent, le nom PI peut ne pas être entré dans la table des symboles. Cela peut être déroutant si vous obtenez une erreur lors de la compilation impliquant l’utilisation de la constante, car le message d’erreur peut se référer à 3.1416, pas à PI. Si PI était défini dans un fichier d’en-tête que vous n’aviez pas écrit, vous n’auriez aucune idée d’où venait cette version 3.1416.

Ce problème peut également se produire dans un débogueur symbolique, car, encore une fois, le nom avec lequel vous programmez peut ne pas figurer dans la table des symboles.

Solution:

 const double PI = 3.1416; //or static const... 

Ne pense pas qu’il y ait une réponse pour “ce qui est toujours le meilleur” mais, comme Matthieu l’a dit

static const

est de type sécurisé. Ma plus grande bête noire avec #define , cependant, est que lorsque vous déboguez dans Visual Studio, vous ne pouvez pas regarder la variable. Cela donne une erreur que le symbole ne peut pas être trouvé.

Incidemment, une alternative à #define , qui fournit une scope correcte mais se comporte comme une constante “réelle”, est “enum”. Par exemple:

 enum {number_ten = 10;} 

Dans de nombreux cas, il est utile de définir des types énumérés et de créer des variables de ces types. Si cela est fait, les débogueurs peuvent afficher des variables en fonction de leur nom d’énumération.

Une mise en garde importante à ce sujet est que: en C ++, les types énumérés ont une compatibilité limitée avec les entiers. Par exemple, par défaut, on ne peut pas effectuer d’arithmétique sur eux. Je trouve que c’est un comportement par défaut curieux pour les énumérations; Bien qu’il aurait été bien d’avoir un type “enum ssortingct”, étant donné le désir de rendre C ++ généralement compatible avec C, je pense que le comportement par défaut d’un type “enum” devrait être interchangeable avec les entiers.

Bien que la question concerne les entiers, il convient de noter que #define et enums sont inutiles si vous avez besoin d’une structure ou d’une chaîne constante. Celles-ci sont généralement transmises aux fonctions en tant que pointeurs. (Avec des cordes c’est nécessaire, avec des structures beaucoup plus efficaces.)

En ce qui concerne les nombres entiers, si vous êtes dans un environnement intégré avec une mémoire très limitée, vous devrez peut-être vous préoccuper de l’emplacement de la constante et de la compilation des access. Le compilateur peut append deux consts au moment de l’exécution, mais append deux #defines au moment de la compilation. Une constante #define peut être convertie en une ou plusieurs instructions MOV [immediate], ce qui signifie que la constante est effectivement stockée dans la mémoire du programme. Une constante const sera stockée dans la section .const de la mémoire de données. Dans les systèmes avec une architecture Harvard, il pourrait y avoir des différences dans les performances et l’utilisation de la mémoire, même si elles seraient probablement faibles. Ils pourraient être importants pour l’optimisation des boucles internes.

Une simple différence:

Au moment du prétraitement, la constante est remplacée par sa valeur. Vous ne pouvez donc pas appliquer l’opérateur de déréférence à une définition, mais vous pouvez appliquer l’opérateur de déréférence à une variable.

Comme vous le supposez, définir est plus rapide que la constante statique.

Par exemple, avoir:

 #define mymax 100 

vous ne pouvez pas faire printf("address of constant is %p",&mymax); .

Mais avoir

 const int mymax_var=100 

vous pouvez faire printf("address of constant is %p",&mymax_var); .

Pour être plus clair, la définition est remplacée par sa valeur au stade du prétraitement, nous n’avons donc aucune variable stockée dans le programme. Nous avons juste le code du segment de texte du programme où la définition a été utilisée.

Cependant, pour statique const, nous avons une variable qui est allouée quelque part. Pour gcc, static const est alloué dans le segment de texte du programme.

Ci-dessus, je voulais parler de l’opérateur de référence, donc remplacez la référence par la référence.

Nous avons examiné le code assembleur produit sur le MBF16X … Les deux variantes produisent le même code pour les opérations arithmétiques (ADD Immediate, par exemple).

Donc, const int est préféré pour le type check alors que #define est un ancien style. Peut-être que c’est spécifique au compilateur. Alors, vérifiez votre code assembleur produit.