Lequel est-il 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 portée et qu'il est conforme aux types.
Le seul inconvénient que j'ai pu voir: si vous voulez 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
Autant que possible, au lieu de macros/Ellipsis, utilisez une alternative sécurisée au type.
Si vous devez vraiment utiliser une macro (par exemple, vous voulez __FILE__
ou __LINE__
), vous feriez mieux de nommer votre macro TRÈS soigneusement: dans sa convention de dénominationBoost recommande toutes les majuscules, en commençant par le nom du projet (ici BOOST_), tout en parcourant la bibliothèque, vous remarquerez que celui-ci est (généralement) suivi du nom du domaine particulier (bibliothèque), puis avec un nom significatif.
Cela fait généralement des noms longs :)
Cela dépend de ce que vous avez besoin de la valeur. Vous (et tous les autres jusqu'ici) avez omis la troisième alternative:
static const int var = 5;
#define var 5
enum { var = 5 };
En ignorant les problèmes liés au choix du nom, alors:
Ainsi, dans la plupart des contextes, préférez l’énumération aux alternatives. Autrement, le premier et le dernier point seront vraisemblablement les facteurs déterminants - et vous devez réfléchir plus avant si vous devez satisfaire les deux à la fois.
Si vous posiez des questions sur le C++, vous utiliseriez 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 portée et de typage d'un objet const
, en réalité, les objets const
en C (par opposition à C++) ne sont pas de vraies constantes et sont donc généralement inutiles dans la plupart des cas.
Ainsi, en C, le choix doit être déterminé par la manière dont vous envisagez d’utiliser votre constante. Par exemple, vous ne pouvez pas utiliser d'objet const int
comme étiquette case
(alors qu'une macro fonctionnera). Vous ne pouvez pas utiliser un objet const int
comme largeur de champ binaire (alors qu'une macro fonctionnera). Dans C89/90, vous ne pouvez pas utiliser d'objet const
pour spécifier une taille de tableau (alors qu'une macro fonctionnera). Même en C99, vous ne pouvez pas utiliser un objet 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 d'autre choix que d'utiliser #define
en C. Et n'oubliez pas une autre alternative, qui produit des constantes vraies dans C - enum
.
En C++, les objets const
sont de vraies constantes. Il est donc presque toujours préférable de préférer la variante const
(il n'est pas nécessaire d'utiliser static
explicite, cependant).
La différence entre static const
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 transmettre l'adresse d'un #define
alors que vous pouvez transmettre 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 des circonstances différentes. 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 autorise pas à utiliser static const
s dans ce contexte, pour autant que je sache. En C++, évitez les macros dans ces cas. Tu peux écrire
const int maxlen = 5;
void foo() {
int bar[maxlen];
}
et même laisser de côté 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 une 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 me ferais un plaisir d'utiliser dactylographié const
dans ces cas, sinon ...
Si vous pouvez vous en tirer, static const
présente de nombreux avantages. Il obéit aux principes de la portée normale, est visible dans un débogueur et obéit généralement aux règles auxquelles les variables obéissent.
Cependant, du 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];
en tant que déclaration, mais vous ne pouvez pas le faire (sauf en tant qu'extension de compilateur "avec static const int var = 5;
. Ce n'est pas le cas en C++, où la version static const
peut être utilisée n'importe où la version #define
peut , et je crois que c'est également le cas avec C99.
Cependant, ne nommez jamais une constante #define
avec un nom en minuscule. Il annulera toute utilisation possible de ce nom jusqu'à la fin de l'unité de traduction. Les constantes macro doivent se trouver dans leur propre espace de noms, qui est généralement composé de lettres majuscules, éventuellement avec un préfixe.
J'ai écrit un programme de test rapide pour démontrer une différence:
#include <stdio.h>
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 compile 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 quand define donne un avertissement.
#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 toutes les constantes #define
s utilisent des majuscules pour éviter les conflits.
Il est TOUJOURS préférable d'utiliser const, au lieu de #define. En effet, 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 même que le code source parvienne à 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 faire référence à 3.1416, pas à PI. Si PI a été défini dans un fichier d’en-tête que vous n’avez pas écrit, vous n’auriez aucune idée de l’origine de ce 3.1416.
Ce problème peut également apparaître 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...
La définition
const int const_value = 5;
ne définit pas toujours une valeur constante. Certains compilateurs (par exemple tcc 0.9.26 ) n'allouent que de la mémoire identifiée par le nom "valeur_const". En utilisant l'identifiant "valeur_comp", 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 compiler.
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.
Incidemment, une alternative à #define
, qui fournit une portée 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 pourront peut-être afficher les variables en fonction de leur nom d'énumération.
Une mise en garde importante à cela, cependant: 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 de calcul arithmétique sur eux. Je trouve que c'est un comportement par défaut curieux pour les enums; alors qu'il aurait été agréable d'avoir un type "strict enum", étant donné le désir d'avoir le C++ généralement compatible avec le C, je pense que le comportement par défaut d'un type "enum" devrait être interchangeable avec des entiers.
Ne pensez pas qu'il y ait une réponse à "ce qui est toujours le meilleur" mais, comme l'a dit Matthieu
static const
est le type sûr. Mon plus gros problème avec #define
, cependant, est lors du débogage dans Visual Studio vous ne pouvez pas regarder la variable. Cela donne une erreur que le symbole ne peut pas être trouvé.
Bien que la question concerne les entiers, il est intéressant de noter que #define et les énumérations sont inutiles si vous avez besoin d'une structure ou d'une chaîne constante. Ce sont généralement les deux passés aux fonctions en tant que pointeurs. (Avec les chaînes, c'est nécessaire; avec les structures, c'est beaucoup plus efficace.)
En ce qui concerne les entiers, si vous vous trouvez dans un environnement embarqué avec une mémoire très limitée, vous devrez peut-être vous soucier de l'emplacement de stockage de la constante et de la manière dont les accès sont compilés. Le compilateur peut ajouter deux consts au moment de l'exécution, mais deux #définis au moment de la compilation. Une constante #define peut être convertie en une ou plusieurs instructions MOV [immédiate], ce qui signifie que la constante est effectivement stockée dans la mémoire de programme. Une constante const sera stockée dans la section .const de la mémoire de données. Dans les systèmes dotés d'une architecture de Harvard, il peut exister des différences de performances et d'utilisation de la mémoire, bien qu'elles soient probablement réduites. Ils pourraient avoir une incidence sur l’optimisation du noyau dur 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érencement à une définition, mais vous pouvez appliquer l'opérateur de déréférencement à une variable.
Comme vous le supposez, définir est plus rapide que const statique.
Par exemple, avoir:
#define mymax 100
vous ne pouvez pas faire printf("address of constant is %p",&mymax);
.
Mais ayant
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 lors de la phase de 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, nous avons une variable qui est allouée quelque part. Pour gcc, les const statiques sont alloués dans le segment de texte du programme.
Ci-dessus, je voulais parler de l'opérateur de référence, remplacez donc déréférencement par référence.
Je ne suis pas sûr d'avoir raison, mais à mon avis, appeler la valeur #define
d est beaucoup plus rapide que d'appeler une autre variable (ou valeur const) normalement déclarée . C'est parce que le programme est en cours d'exécution et qu'il doit utiliser une variable déclarée normalement doit sauter à la place exacte dans la mémoire pour obtenir cette variable.
En revanche, lorsqu'il utilise la valeur #define
d, le programme n'a pas besoin de passer à la mémoire allouée, il prend simplement la valeur. Si #define myValue 7
et le programme appelant myValue
, il se comporte exactement comme s'il appelait simplement 7
.
Nous avons examiné le code assembleur produit sur le MBF16X ... Les deux variantes donnent le même code pour les opérations arithmétiques (ADD Immediate, par exemple).
Donc, const int
est préféré pour la vérification de type, alors que #define
est un style ancien. Peut-être que c'est spécifique au compilateur. Alors vérifiez votre code assembleur produit.