web-dev-qa-db-fra.com

"statique fixe" vs "#define" vs "enum"

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 };
513
Vijay

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 :)

264
Matthieu M.

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:

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

En ignorant les problèmes liés au choix du nom, alors:

  • Si vous devez faire passer un pointeur, vous devez utiliser (1).
  • Puisque (2) est apparemment une option, vous n'avez pas besoin de passer des pointeurs.
  • Les symboles (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 des tableaux de portée globale; les deux (2) et (3) peuvent.
  • (1) ne peut pas être utilisé comme dimension pour les tableaux statiques dans la portée 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 peuvent pas être utilisés dans des endroits tels que les instructions switch; 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 modifier le code que vous ne souhaitiez pas modifier car il est utilisé par le pré-processeur; (1) et (3) n'auront pas d'effets secondaires inattendus comme ceux-là.
  • Vous pouvez détecter si (2) a été défini dans le préprocesseur; ni (1) ni (3) ne le permettent.

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.

620
Jonathan Leffler

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).

100
AnT

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 ... :-)

28
wrapperm

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 consts 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].

14
sellibitze

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 ...

13
Gauthier

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. 

9
David Thornley

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.

6
Michael Potter

#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 #defines 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...
5
suren

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.

4
user2229691

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.

3
supercat

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é.

3
Afcrowe

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.

2
Adam Haun

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.

1
mihaitzateo

Je ne suis pas sûr d'avoir raison, mais à mon avis, appeler la valeur #defined 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 #defined, 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.

0
pajczur

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.

0
guest