Pourquoi int x est-il faux, où n est une valeur const?

Je ne comprends pas pourquoi cela est faux:

const int n = 5; int x[n] = { 1,1,3,4,5 }; 

même si n est déjà une valeur const.

Cela semble correct pour le compilateur GNU:

 const int n = 5; int x[n]; /*without initialization*/ 

Je connais la fonctionnalité VLA de C99 et je pense que cela est lié à ce qui se passe, mais il me faut juste des éclaircissements sur ce qui se passe à l’arrière-plan.

L’essentiel à retenir est que const et “constant” signifient deux choses bien différentes.

Le mot clé const signifie vraiment “lecture seule”. Une constante est un littéral numérique, tel que 42 ou 1.5 (ou une constante d’énumération ou de caractère). Une expression constante est un type particulier d’expression qui peut être évalué au moment de la compilation, tel que 2 + 2 .

Donc donné une déclaration:

 const int n = 5; 

l’expression n fait référence à la valeur de l’ object et n’est pas traitée comme une expression constante. Un compilateur typique optimisera une référence à n en le remplaçant par le même code qu’il utiliserait pour un 5 littéral, mais ce n’est pas obligatoire – et les règles pour déterminer si une expression est constante sont déterminées par le langage et non par l’habileté de le compilateur actuel.

Voici un exemple de la différence entre const (en lecture seule) et constante (évaluée au moment de la compilation):

 const size_t now = time(NULL); 

Le mot-clé const signifie que vous n’êtes pas autorisé à modifier la valeur de now après son initialisation, mais que la valeur de time(NULL) ne peut évidemment pas être calculée avant l’exécution.

Donc ça:

 const int n = 5; int x[n]; 

n’est pas plus valable en C qu’il ne le serait sans le mot-clé const .

Le langage pourrait (et IMHO probablement le devrait) évaluer n comme une expression constante; ce n’est tout simplement pas défini de cette façon. (C ++ a une telle règle; voir la norme C ++ ou une référence décente pour les détails sanglants.)

Si vous voulez une constante nommée avec la valeur 5 , le moyen le plus courant consiste à définir une macro:

 #define N 5 int x[N]; 

Une autre approche consiste à définir une constante d’énumération:

 enum { n = 5 }; int x[n]; 

Les constantes d’énumération sont des expressions constantes et sont toujours du type int (ce qui signifie que cette méthode ne fonctionnera pas pour les types autres que int ). Et c’est sans doute un abus du mécanisme d’ enum .

À partir de la norme de 1999, un tableau peut être défini avec une taille non constante. c’est un VLA ou un tableau de longueur variable. De tels tableaux ne sont autorisés qu’à la scope d’un bloc et peuvent ne pas avoir d’initialiseurs (car le compilateur est incapable de vérifier que l’initialiseur contient le nombre correct d’éléments).

Mais compte tenu de votre code d’origine:

 const int n = 5; int x[n] = { 1,1,3,4,5 }; 

vous pouvez laisser le compilateur déduire la longueur de l’initialiseur:

 int x[] = { 1,1,3,4,5 }; 

Et vous pouvez ensuite calculer la longueur à partir de la taille du tableau:

 const int x_len = sizeof x / sizeof x[0]; 

Pourquoi int x[n] est faux, où n est une valeur const ?

n n’est pas une constante. const ne fait que promettre que n est une variable ‘en lecture seule’ qui ne devrait pas être modifiée pendant l’exécution du programme.
Notez que dans c , contrairement à c ++ , les variables qualifiées const ne sont pas constantes. Par conséquent, le tableau déclaré est un tableau de longueur variable.
Vous ne pouvez pas utiliser la liste d’initialisation pour initialiser des tableaux de longueur variable.

C11-§7.7.9 / 3:

Le type de l’entité à initialiser doit être un tableau de taille inconnue ou un type d’object complet qui n’est pas un type de tableau de longueur variable .

Vous pouvez utiliser #define ou enum pour faire de n une constante

 #define n 5 int x[n] = { 1,1,3,4,5 }; 

Si vous initialisez complètement un tableau, il est alors plus facile, plus sûr et plus facile à gérer de laisser le compilateur déduire la taille du tableau:

 int x[] = { 1,1,3,4,5 }; const int n = sizeof(x) / sizeof(*x) ; 

Ensuite, pour changer la taille du tableau, il vous suffit de changer le nombre d’initialiseurs, plutôt que de changer la taille et la liste d’initialisateurs afin qu’ils correspondent. Particulièrement utile quand il y a beaucoup d’initialiseurs.

Même si n est un const , vous ne pouvez pas l’utiliser pour définir la taille d’un tableau, sauf si vous souhaitez créer un VLA. Cependant, vous ne pouvez pas utiliser une liste d’initialiseur pour initialiser un VLA.

Utilisez une macro pour créer un tableau de taille fixe.

 #define ARRAY_SIZE 5 int x[ARRAY_SIZE] = { 1,1,3,4,5 }; 

Votre code est-il sémantiquement différent de myfunc() ici:

 void myfunc(const int n) { int x[n] = { 1,1,3,4,5 }; printf("%d\n", x[n-1]); *( (int *) &n) = 17; // Somewhat less "constant" than hoped... return ; } int main(){ myfunc(4); myfunc(5); myfunc(6); // Haven't actually tested this. Boom? Maybe just printf(noise)? return 0; } 

n est-ce vraiment tout ce qui est constant? A votre avis, combien d’espace le compilateur devrait-il allouer à x[] (puisqu’il appartient au compilateur de le faire)?

Comme d’autres l’ont souligné, le qualificatif cv- const ne signifie pas “une valeur constante pendant la compilation et pour tous les temps après”. Cela signifie “une valeur que le code local n’est pas censé changer (bien qu’il puisse le faire)”.