Tableau varié à la portée du fichier

85

Je souhaite créer un tableau statique constant à utiliser dans mon fichier d'implémentation Objective-C similaire à quelque chose comme celui-ci au niveau supérieur de mon fichier ".m":

static const int NUM_TYPES = 4;
static int types[NUM_TYPES] = { 
  1,
  2, 
  3, 
  4 };

Je prévois d'utiliser NUM_TYPESplus tard dans le fichier, je voulais donc le mettre dans une variable.

Cependant, lorsque je fais cela, j'obtiens l'erreur

"Types" modifiés de manière variable à la portée du fichier "

Je suppose que cela peut avoir quelque chose à voir avec la taille du tableau étant une variable (je ne reçois pas ce message lorsque j'y mets un littéral entier, comme static int types[4]).

Je veux résoudre ce problème, mais peut-être que je fais tout faux ... J'ai 2 objectifs ici:

  1. Pour avoir un tableau accessible dans tout le fichier
  2. Pour encapsuler NUM_TYPESdans une variable afin que je n'ai pas le même littéral dispersé à différents endroits dans mon fichier

Aucune suggestion?

[EDIT] J'ai trouvé ceci dans la FAQ C: http://c-faq.com/ansi/constasconst.html

Sam
la source
2
Que se passe-t-il si vous le faites comme une définition à la place? #define kNUM_TYPES 4?
Jorge Israel Peña
Cela fonctionne ... pour une raison quelconque, j'essayais de ne pas utiliser le préprocesseur parce que je pensais me souvenir d'avoir lu ça quelque part, mais j'ai juste fait quelques recherches supplémentaires et je n'ai pas trouvé de bonne raison de ne pas l'utiliser dans ce cas. Je pense que cela peut être moins souhaitable si je crée des objets dans le préprocesseur (comme @"An NSString literal") Le seul problème avec votre morceau de code est qu'il n'y a pas besoin de point-virgule.
Sam
Ah oui, merci pour la mise en garde et heureux de pouvoir vous aider.
Jorge Israel Peña

Réponses:

62

La raison de cet avertissement est que const dans c ne signifie pas constante. Cela signifie «lecture seule». Ainsi, la valeur est stockée à une adresse mémoire et pourrait potentiellement être modifiée par le code machine.

larsr
la source
3
La modification d'un objet défini const(par exemple en éloignant constun pointeur et en stockant une valeur) est un comportement indéfini; par conséquent, la valeur d'un tel objet est une constante de compilation ou d'exécution (en fonction de la durée de stockage). La valeur ne peut pas être utilisée dans une expression constante simplement parce que la norme C ne dit pas qu'elle peut l'être. (Le rejet constet le stockage d'une valeur sont autorisés si l'objet de destination est défini sans constou alloué dynamiquement; les chaînes littérales ne le sont pas constmais ne peuvent pas être écrites.)
jilles
3
@jilles "pourrait potentiellement être modifié par le code machine" ne signifie pas que l'auteur de cette réponse voulait dire "pourrait potentiellement être modifié par le code C". De plus, cela a une autre très bonne raison: il peut y avoir des externconstantes dans différentes TU dont la valeur n'est pas connue lors de la compilation de la TU actuelle.
14
Un moyen d'améliorer cette réponse serait de montrer comment résoudre ce problème.
George Stocker
32

Si vous allez quand même utiliser le préprocesseur, comme pour les autres réponses, vous pouvez faire en sorte que le compilateur détermine la valeur de NUM_TYPESautomatiquement:

#define NUM_TYPES (sizeof types / sizeof types[0])
static int types[] = { 
  1,
  2, 
  3, 
  4 };
caf
la source
Wow c'est vraiment cool ... Je ne savais pas que c'était possible. Je suppose que le coût de ce calcul est négligeable. Puis-je également supposer qu'un compilateur pourrait optimiser cela à une valeur statique?
Sam
2
Oui, le résultat de sizeofsur des objets comme celui-ci est une constante de compilation.
caf
11

Il est également possible d'utiliser l'énumération.

typedef enum {
    typeNo1 = 1,
    typeNo2,
    typeNo3,
    typeNo4,
    NumOfTypes = typeNo4
}  TypeOfSomething;
Dave L Delaney
la source
4

Comme cela est déjà expliqué dans d'autres réponses, consten C signifie simplement qu'une variable est en lecture seule. Il s'agit toujours d'une valeur d'exécution. Cependant, vous pouvez utiliser an enumcomme constante réelle dans C:

enum { NUM_TYPES = 4 };
static int types[NUM_TYPES] = { 
  1, 2, 3, 4
};
CygnusX1
la source
3

Imho c'est une faille dans de nombreux compilateurs c. Je sais pertinemment que les compilateurs avec lesquels j'ai travaillé ne stockent pas une variable "static const" à une adresse mais remplacent l'utilisation dans le code par la très constante. Cela peut être vérifié car vous obtiendrez la même somme de contrôle pour le code produit lorsque vous utilisez une directive de préprocesseurs #define et lorsque vous utilisez une variable statique const.

Dans tous les cas, vous devez utiliser des variables const statiques au lieu de #defines chaque fois que possible car le const statique est de type sécurisé.

hans lepoeter
la source
Cela semble assez mauvais, car vous pouvez prendre l'adresse d'une static constvariable. Le comportement que vous décrivez peut être une optimisation valide, mais ce n'est certainement pas quelque chose qui pourrait toujours fonctionner.
détendre
C'est vraiment bien. Le compilateur C peut remplacer les utilisations individuelles des variables globales const par la valeur constante chaque fois que possible. Si toutes les références à une variable sont converties en constantes, le compilateur peut la supprimer entièrement. Si vous utilisez l'adresse n'importe où, elle ne sera pas supprimée. Rien de tout cela ne change que selon la norme du langage, C n'autorise pas les tableaux globaux avec une variable comme taille, que la variable soit const ou non.
Evan