Dans de nombreux programmes, un #define
sert le même objectif qu'une constante. Par exemple.
#define FIELD_WIDTH 10
const int fieldWidth = 10;
Je vois généralement le premier formulaire préféré à l'autre, en s'appuyant sur le pré-processeur pour gérer ce qui est essentiellement une décision d'application. Y a-t-il une raison à cette tradition?
Il y a une raison très solide à cela: const
en C ne signifie pas que quelque chose est constant. Cela signifie simplement qu'une variable est en lecture seule.
Dans les endroits où le compilateur requiert une vraie constante (comme pour les tailles de tableau pour les tableaux non VLA), l'utilisation d'une variable const
, telle que fieldWidth
n'est tout simplement pas possible.
Ils sont différents.
const
n'est qu'un qualificatif, qui indique qu'une variable ne peut pas être modifiée au moment de l'exécution. Mais toutes les autres caractéristiques de la variable persistent: elle a alloué du stockage, et ce stockage peut être adressé. Le code ne le traite donc pas uniquement comme un littéral, mais fait référence à la variable en accédant à l'emplacement de mémoire spécifié (sauf s'il s'agit de static const
, il peut ensuite être optimisé) et charger sa valeur au moment de l'exécution. Et comme une variable const
a alloué du stockage, si vous l'ajoutez à un en-tête et l'incluez dans plusieurs sources C, vous obtiendrez une erreur de liaison "définition de symboles multiples" sauf si vous la marquez comme extern
. Et dans ce cas, le compilateur ne peut pas optimiser le code par rapport à sa valeur réelle (sauf si l'optimisation globale est activée).
#define
remplace simplement un nom par sa valeur. De plus, un #define
'd constante peut être utilisée dans le préprocesseur: vous pouvez l'utiliser avec #ifdef
pour effectuer une compilation conditionnelle en fonction de sa valeur, ou utilisez l'opérateur de chaîne #
pour obtenir une chaîne avec sa valeur. Et comme le compilateur connaît sa valeur au moment de la compilation, il peut optimiser le code en fonction de cette valeur.
Par exemple:
#define SCALE 1
...
scaled_x = x * SCALE;
Lorsque SCALE
est défini comme 1
le compilateur peut éliminer la multiplication car il sait que x * 1 == x
, mais si SCALE
est un (extern
) const
, il devra générer du code pour récupérer la valeur et effectuer la multiplication car la valeur ne sera connue que l'étape de liaison. (extern
est nécessaire pour utiliser la constante de plusieurs fichiers source.)
Un équivalent plus proche de l'utilisation de #define
utilise des énumérations:
enum dummy_enum {
constant_value = 10010
};
Mais cela est limité aux valeurs entières et n'a pas les avantages de #define
, il n'est donc pas largement utilisé.
const
est utile lorsque vous devez importer une valeur constante à partir d'une bibliothèque dans laquelle elle a été compilée. Ou si elle est utilisée avec des pointeurs. Ou s'il s'agit d'un tableau de valeurs constantes accessibles via une valeur d'index variable. Sinon, const
n'a aucun avantage sur #define
.
La raison en est que la plupart du temps, vous voulez une constante, pas une variable qualifiée const
. Les deux ne sont pas à distance les mêmes dans le langage C. Par exemple, les variables ne sont pas valides dans le cadre des initialiseurs pour les objets static
- durée de stockage, en tant que dimensions de tableau non-vla (par exemple, la taille d'un tableau dans une structure ou tout tableau antérieur à C99).
Développant un peu la réponse de R: fieldWidth
n'est pas un expression constante; c'est une variable qualifiée const
. Son valeur n'est pas établi avant l'exécution, il ne peut donc pas être utilisé lorsqu'une expression constante de compilation est requise (comme dans une déclaration de tableau ou une étiquette de casse dans un switch
déclaration, etc.).
Comparer avec la macro FIELD_WIDTH
, qui après le prétraitement se développe en l'expression constante 10
; cette valeur est connue au moment de la compilation, elle peut donc être utilisée pour les dimensions de tableau, les étiquettes de cas, etc.
Pour ajouter à la réponse de R. et Bart: il n'y a qu'une seule façon de définir des constantes de temps de compilation symboliques dans les constantes de type énumération C :. La norme impose que ceux-ci soient de type int
. Personnellement, j'écrirais votre exemple comme
enum { fieldWidth = 10 };
Mais je suppose que le goût diffère beaucoup parmi les programmeurs C à ce sujet.
Bien qu'un const int ne soit pas toujours approprié, une énumération fonctionnera généralement comme un substitut à #define si vous définissez quelque chose comme étant une valeur intégrale. C'est en fait ma préférence dans un tel cas.
enum { FIELD_WIDTH = 16384 };
char buf[FIELD_WIDTH];
En C++, c'est un énorme avantage car vous pouvez étendre votre énumération dans une classe ou un espace de noms, alors que vous ne pouvez pas étendre un #define.
En C, vous n'avez pas d'espaces de noms et vous ne pouvez pas étendre une énumération à l'intérieur d'une structure, et je ne suis même pas sûr que vous obtenez la sécurité de type, donc je ne vois aucun avantage majeur, bien que peut-être un programmeur C me le fasse remarquer .
Selon K&R (2e édition, page 211), "les propriétés const et volatiles sont nouvelles avec la norme ANSI". Cela peut impliquer que le code ANSI vraiment ancien n'avait pas ces mots-clés et c'est vraiment une question de tradition. De plus, il indique qu'un compilateur doit détecter les tentatives de modification des variables const mais à part cela, il peut ignorer ces qualificatifs. Je pense que cela signifie que certains compilateurs peuvent ne pas optimiser le code contenant la variable const à représenter comme valeur intermédiaire dans le code machine (comme le fait #define) et cela pourrait coûter plus de temps pour accéder à la mémoire distante et affecter les performances.
Certains compilateurs C stockent toutes les variables const
dans le binaire, qui si elles préparent ne grande liste de coefficients peut utiliser une énorme quantité d'espace dans le monde embarqué.
Inversement: l'utilisation de const
permet de flasher un programme existant pour modifier des paramètres spécifiques.
La meilleure façon de définir des constantes numériques en C est d'utiliser enum. Lisez le chapitre correspondant du langage de programmation C de K&R, page 39.