Lequel est préférable d'utiliser parmi les déclarations ci-dessous en C?
static const int var = 5;
ou
#define var 5
ou
enum { var = 5 };
Lequel est préférable d'utiliser parmi les déclarations ci-dessous en C?
static const int var = 5;
ou
#define var 5
ou
enum { var = 5 };
Réponses:
Cela dépend de la valeur dont vous avez besoin. Vous (et tout le monde jusqu'à présent) avez omis la troisième alternative:
static const int var = 5;
#define var 5
enum { var = 5 };
Ignorer les problèmes liés au choix du nom, puis:
Donc, dans la plupart des contextes, préférez l'énumération aux alternatives. Sinon, le premier et le dernier point sont probablement les facteurs déterminants - et vous devez réfléchir davantage si vous devez satisfaire les deux à la fois.
Si vous posiez des questions sur C ++, alors vous utiliseriez l'option (1) - la constante statique - à chaque fois.
enum
est qu'ils sont implémentés en tant que int
([C99] 6.7.2.2/3). A #define
vous permet de spécifier non signé et long avec U
et L
suffixes, et const
vous permet de donner un type. enum
peut provoquer des problèmes avec les conversions de type habituelles.
enum
ni #define
n'utilise d'espace supplémentaire en soi. La valeur apparaîtra dans le code objet dans le cadre des instructions plutôt que d'être allouée au stockage dans le segment de données ou dans le segment de mémoire ou sur la pile. Vous aurez de l'espace alloué pour le static const int
, mais le compilateur pourrait l'optimiser si vous ne prenez pas d'adresse.
enum
s (et static const
): ils ne peuvent pas être modifiés. a define
peut être #undefine
'd où an enum
et static const
sont fixés à la valeur donnée.
En général:
static const
Parce qu'il respecte la portée et est de type sécurisé.
La seule mise en garde 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
Dans la mesure du possible, au lieu de macros / points de suspension, utilisez une alternative de type sécurisé.
Si vous avez vraiment besoin d'aller avec une macro (par exemple, vous voulez __FILE__
ou __LINE__
), alors vous feriez mieux de nommer votre macro TRÈS attentivement: dans sa convention de dénomination, Boost recommande tout en majuscules, en commençant par le nom du projet (ici BOOST_ ), en parcourant la bibliothèque, vous remarquerez qu'il 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 :)
static
dont l'adresse est prise doivent rester; et si l'adresse est prise, on n'aurait pas pu utiliser un #define
ou enum
(pas d'adresse) ... donc je ne vois vraiment pas quelle alternative aurait pu être utilisée. Si vous pouvez vous débarrasser de "l'évaluation du temps de compilation", vous cherchez peut-être à la extern const
place.
#if
peut-être préférable #ifdef
aux booléens, mais dans ce cas, il serait impossible de définir var
à 0
partir de la ligne de commande. Donc, dans ce cas, #ifdef
cela a plus de sens, tant qu'il 0
y a une valeur légale pour var
.
En C, plus précisément? En C, la bonne réponse est: utiliser #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 const
objet, en réalité, les const
objets en C (par opposition à C ++) ne sont pas de vraies constantes et sont donc généralement inutiles dans la plupart des cas pratiques.
Ainsi, en C, le choix doit être déterminé par la façon dont vous prévoyez d'utiliser votre constante. Par exemple, vous ne pouvez pas utiliser un const int
objet comme case
étiquette (alors qu'une macro fonctionnera). Vous ne pouvez pas utiliser un const int
objet comme largeur de champ binaire (alors qu'une macro fonctionnera). En C89 / 90, vous ne pouvez pas utiliser un const
objet pour spécifier une taille de tableau (alors qu'une macro fonctionnera). Même en C99, vous ne pouvez pas utiliser un const
objet pour spécifier une taille de tableau lorsque vous avez besoin d'un tableau non 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
en C. Et n'oubliez pas une autre alternative, qui produit de vraies constantes en C - enum
.
En C ++, les const
objets sont de vraies constantes, donc en C ++ il est presque toujours préférable de préférer la const
variante (pas besoin d'explicite static
en C ++ cependant).
const int
objets dans les étiquettes de cas est illégale dans toutes les versions du langage C. (Bien sûr, votre compilateur est libre de le prendre en charge comme une extension de langage non standard de type C ++.)
const
signifie en lecture seule. const int r = rand();
est parfaitement légal.
constexpr
par rapport à const
spécialement avec les stl
conteneurs comme array
ou bitset
.
switch()
déclaration, pas dans case
un. Je viens juste de m'attraper sur celui-ci aussi
La différence entre static const
et #define
est que le premier utilise la mémoire et 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 sélectionner un parmi ces deux. Les deux sont à leur meilleur dans des circonstances différentes. Veuillez ne pas supposer que l'un est meilleur que l'autre ... :-)
Si cela avait été le cas, Dennis Ritchie aurait gardé le meilleur seul ... hahaha ... :-)
const
mémoire utilise. GCC (testé avec 4.5.3 et quelques versions plus récentes) optimise facilement le const int
en un littéral direct dans votre code lors de l'utilisation de -O3. Donc, si vous faites du développement intégré à faible RAM (par exemple AVR), vous pouvez utiliser en toute sécurité les constantes C si vous utilisez GCC ou un autre compilateur compatible. Je ne l'ai pas testé mais je m'attends à ce que Clang fasse la même chose.
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
s dans ce contexte pour autant que je sache. 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 laisser de côté static
parce que la liaison interne est impliquée par const
déjà [en C ++ uniquement].
const int MY_CONSTANT = 5;
dans un fichier et y accéder avec extern const int MY_CONSTANT;
dans un autre. Je n'ai pu trouver aucune information dans la norme (C99 au moins) sur la const
modification du comportement par défaut "6.2.2: 5 Si la déclaration d'un identificateur pour un objet a une portée de fichier et aucun spécificateur de classe de stockage, sa liaison est externe".
bar
est un VLA (tableau de longueur variable); le compilateur est susceptible de générer du code comme si sa longueur était constante.
Un autre inconvénient de const
C est que vous ne pouvez pas utiliser la valeur pour en 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 car 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 heureux d'utiliser tapé const
dans ces cas, sinon ...
static uint8_t const ARRAY_SIZE = 16;
compilation soudaine ne peut plus être un peu difficile, en particulier lorsque le #define ARRAY_SIZE 256
est enfoui à dix couches profondément dans un réseau d'en-têtes emmêlé. Ce nom tout en majuscules ARRAY_SIZE
demande des ennuis. Réservez ALL_CAPS pour les macros et ne définissez jamais une macro qui n'est pas sous la forme ALL_CAPS.
const
. Cela pourrait être plus voté!
Si vous pouvez vous en tirer, cela static const
présente de nombreux avantages. Il obéit aux principes de portée normaux, 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 réellement 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 de compilateur "avec static const int var = 5;
. Ce n'est pas le cas en C ++, où la static const
version peut être utilisée partout où la #define
version le peut, et je crois que cela c'est aussi le cas avec C99.
Cependant, ne nommez jamais une #define
constante avec un nom en minuscule. Il remplacera toute utilisation possible de ce nom jusqu'à la fin de l'unité de traduction. Les macros constantes doivent être dans ce qui est effectivement leur propre espace de noms, qui est traditionnellement composé de majuscules, peut-être avec un préfixe.
const
en C99 n'est pas encore une vraie constante. Vous pouvez déclarer la taille du tableau avec un const
dans C99, mais uniquement parce que C99 prend en charge les tableaux à longueur variable. Pour cette raison, cela ne fonctionnera que là où les VLA sont autorisés. Par exemple, même en C99, vous ne pouvez toujours pas utiliser a const
pour déclarer la taille d'un tableau de membres dans a struct
.
const int
taille comme s'il s'agissait d'une const C ++ ou d'une macro. Que vous souhaitiez dépendre de cet écart de GCC par rapport à la norme est bien sûr votre choix, j'irais personnellement avec lui à moins que vous ne puissiez vraiment prévoir d'utiliser un autre compilateur que GCC ou Clang, ce dernier a la même fonctionnalité ici (testé avec Clang 3.7).
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 (en gros).
Exemple:
#define PI 3.1416
Le nom symbolique PI peut ne jamais être vu par les compilateurs; il peut être supprimé par le préprocesseur avant même que le code source ne 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 était défini dans un fichier d'en-tête que vous n'avez pas écrit, vous n'auriez aucune idée d'où vient 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...
#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 se compilera pas. Pour cette raison, le style de codage traditionnel suggère que toutes les constantes #define
utilisent des majuscules pour éviter les conflits.
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 se 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 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 avec 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 compiler.
Cela signifie que la définition
#define CONST_VALUE 5
est le seul moyen de définir une valeur constante qui ne peut en aucun cas être modifiée.
#define
peut également être modifié, en modifiant le code machine.
5
. Mais on ne peut pas modifier #define
car c'est une macro de préprocesseur. Il n'existe pas dans le programme binaire. Si l'on voulait modifier tous les endroits où il CONST_VALUE
était utilisé, il fallait le faire un par un.
#define CONST 5
, alors if (CONST == 5) { do_this(); } else { do_that(); }
, et que le compilateur élimine la else
branche. Comment proposez-vous de modifier le code machine pour passer CONST
à 6?
#define
n'est pas à l'épreuve des balles.
#define
. La seule vraie façon de le faire est d'éditer le code source et de recompiler.
Bien que la question porte sur les entiers, il convient de noter que #define et les énumérations sont inutiles si vous avez besoin d'une structure ou d'une chaîne constante. Ceux-ci sont généralement transmis aux fonctions en tant que pointeurs. (Avec les chaînes, c'est obligatoire; avec les structures, c'est beaucoup plus efficace.)
En ce qui concerne les entiers, si vous êtes dans un environnement intégré 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 façon dont les accès sont compilés. Le compilateur peut ajouter deux consts au moment de l'exécution, mais ajouter deux #defines au moment de la compilation. Une constante #define peut être convertie en une ou plusieurs instructions MOV [immédiates], 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 de performances et d'utilisation de la mémoire, bien qu'elles soient probablement petites. Ils peuvent être importants pour l'optimisation du noyau dur des boucles internes.
Ne pensez pas qu'il y ait une réponse pour "ce qui est toujours le meilleur" mais, comme l'a dit Matthieu
static const
est sûr de type. Ma plus grande bête noire #define
, cependant, est que lors du débogage dans Visual Studio, vous ne pouvez pas regarder la variable. Cela donne une erreur indiquant que le symbole est introuvable.
Soit dit en passant, une alternative à #define
, qui fournit une portée appropriée 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 les variables en fonction de leur nom d'énumération.
Cependant, une mise en garde importante: 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; alors qu'il aurait été bien d'avoir un type "enum strict", étant donné le désir d'avoir C ++ généralement compatible avec C, je pense que le comportement par défaut d'un type "enum" devrait être interchangeable avec des entiers.
int
, donc le "hack d'énumération" ne peut pas être utilisé avec d'autres types entiers. (Le type d' énumération est compatible avec certains types entiers définis par l'implémentation, pas nécessairement int
, mais dans ce cas, le type est anonyme, donc cela n'a pas d'importance.)
int
une variable de type énumération (que les compilateurs sont autorisés à faire) et on essaie d'assigner à une telle variable un membre de sa propre énumération. Je souhaite que les comités de normalisation ajoutent des moyens portables de déclarer des types entiers avec une sémantique spécifiée. N'IMPORTE QUELLE plate-forme, quelle que soit char
sa taille, devrait être en mesure par exemple de déclarer un type qui encapsulera le mod 65536, même si le compilateur doit ajouter de nombreuses AND R0,#0xFFFF
instructions équivalentes.
uint16_t
, bien que ce ne soit bien sûr pas un type d'énumération. Il serait bien de laisser l'utilisateur spécifier le type entier utilisé pour représenter un type d'énumération donné, mais vous pouvez obtenir à peu près le même effet avec un typedef
pour uint16_t
et une série de #define
s pour les valeurs individuelles.
2U < -1L
comme vraies et d'autres comme fausses, et nous sommes maintenant coincés avec le fait que certaines plates-formes implémenteront une comparaison entre uint32_t
et int32_t
comme signé et certains comme non signés, mais cela ne signifie pas que le Comité n'a pas pu définir un successeur de C vers le haut compatible qui inclut des types dont la sémantique serait cohérente sur tous les compilateurs.
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 à un define, mais vous pouvez appliquer l'opérateur de déréférence à une variable.
Comme vous le supposez, define est plus rapide que const statique.
Par exemple, avoir:
#define mymax 100
tu ne peux pas faire printf("address of constant is %p",&mymax);
.
Mais ayant
const int mymax_var=100
tu peux faire printf("address of constant is %p",&mymax_var);
.
Pour être plus clair, le define est remplacé par sa valeur au stade du prétraitement, donc nous n'avons 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 const 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, alors remplacez la déréférence par la référence.
const
qualificatif. C n'a pas de constantes symboliques autres que les constantes enum . A const int
est une variable. Vous confondez également le langage et les implémentations spécifiques. Il n'y a aucune exigence où placer l'objet. Et ce n'est même pas vrai pour gcc: il place généralement const
des variables qualifiées dans la .rodata
section. Mais cela dépend de la plate-forme cible. Et vous voulez dire l'adresse de l'opérateur &
.
Nous avons regardé 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).
Il const int
est donc préférable pour la vérification de type alors qu'il #define
est à l'ancienne. C'est peut-être spécifique au compilateur. Vérifiez donc votre code assembleur produit.
Je ne sais pas si j'ai raison mais à mon avis, appeler #define
valeur d est beaucoup plus rapide que d'appeler toute autre variable normalement déclarée (ou valeur const). C'est parce que lorsque le programme est en cours d'exécution et qu'il doit utiliser une variable normalement déclarée, il doit sauter à l'endroit exact en mémoire pour obtenir cette variable.
En revanche, lorsqu'il utilise la #define
valeur 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 de la même manière que lorsqu'il appelle simplement 7
.