Question "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 };

481
2017-11-04 14:19


origine


Réponses:


En général:

static const

Parce qu'il respecte la portée et est sûr pour le type.

La seule mise en garde que je pouvais 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 / ellipses, utilisez une alternative de type sécurité.

Si vous avez vraiment besoin d'aller avec une macro (par exemple, vous voulez __FILE__ ou __LINE__), alors vous devriez mieux nommer votre macro TRÈS soigneusement: dans sa convention de nommage  Renforcer recommande tous 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 :)


254
2017-11-04 14:21



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 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 à portée 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 vouliez pas changé parce qu'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 posez des questions sur le C ++, vous utiliserez l'option (1) - le const statique - à chaque fois.


578
2017-11-04 15:17



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

Bien qu'il soit avantageux d'avoir les propriétés de cadrage et de saisie d'un const objet, en réalité const les 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.

Donc, en C, le choix devrait être déterminé par la façon dont vous prévoyez d'utiliser votre constante. Par exemple, vous ne pouvez pas utiliser un const intobjet en tant que case label (alors qu'une macro fonctionnera). Vous ne pouvez pas utiliser un const int objet comme une largeur de champ de bits (tandis qu'une macro fonctionnera). En C89 / 90, vous ne pouvez pas utiliser un const object 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 nonVLA tableau.

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 ne pas oublier une autre alternative, qui produit de vraies constantes en C - enum.

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


93
2017-11-04 14:33



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


28
2017-11-05 15:36



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 consts 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 laisser de côté static parce que le lien interne est impliqué par const déjà [en C ++ seulement].


14
2017-11-04 14:32



Un autre inconvénient de const en C est que vous ne pouvez pas utiliser la valeur en initialisant 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 heureux d'utiliser dactylographié const dans ces cas, sinon ...


13
2018-01-19 10:27



Si vous pouvez vous en tirer, static const a beaucoup d'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 originale, ce n'est pas réellement une constante. Si tu utilises #define var 5, tu peux écrire int foo[var]; comme une déclaration, mais vous ne pouvez pas le faire (sauf comme une extension de compilateur "avec static const int var = 5;. Ce n'est pas le cas en C ++, où le static const la version peut être utilisée partout où le #define version peut, et je crois que c'est aussi le cas avec C99.

Cependant, ne jamais nommer un #define constante 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.


9
2017-11-04 14:38



#define var 5 vous causer des ennuis 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 toute constante #defines utilise des lettres majuscules pour éviter les conflits.


5
2018-05-13 23:16



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.


5
2018-04-29 17:37



Ne pensez pas qu'il y a une réponse à "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 lors du débogage dans Visual Studio vous ne pouvez pas regarder la variable. Il donne une erreur que le symbole ne peut pas être trouvé.


3
2017-11-04 14:25



Incidemment, une alternative à #define, qui fournit une portée correcte mais se comporte comme une "vraie" constante, 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 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 d’arithmétique sur eux. Je trouve que c'est un curieux comportement par défaut pour les enums; alors qu'il aurait été agréable d'avoir un type "enum strict", étant donné le désir d'avoir C ++ généralement compatible avec C, je penserais que le comportement par défaut d'un type "enum" devrait être interchangeable avec des entiers.


3
2018-01-01 18:00