Erreur "l'élément d'initialisation n'est pas constant" lors de la tentative d'initialisation de la variable avec const


187

J'obtiens une erreur à la ligne 6 (initialisez my_foo à foo_init) du programme suivant et je ne suis pas sûr de comprendre pourquoi.

typedef struct foo_t {
    int a, b, c;
} foo_t;

const foo_t foo_init = { 1, 2, 3 };
foo_t my_foo = foo_init;

int main()
{
    return 0;
}

Gardez à l'esprit qu'il s'agit d'une version simplifiée d'un plus grand projet multi-fichiers sur lequel je travaille. Le but était d'avoir une seule constante dans le fichier objet, que plusieurs fichiers pourraient utiliser pour initialiser une structure d'état. Comme il s'agit d'une cible intégrée avec des ressources limitées et que la structure n'est pas si petite, je ne veux pas plusieurs copies de la source. Je préfère ne pas utiliser:

#define foo_init { 1, 2, 3 }

J'essaie également d'écrire du code portable, j'ai donc besoin d'une solution valide C89 ou C99.

Cela a-t-il à voir avec les ORG dans un fichier objet? Que les variables initialisées vont dans un ORG et sont initialisées en copiant le contenu d'un deuxième ORG?

Peut-être que j'aurai juste besoin de changer de tactique et qu'une fonction d'initialisation fasse toutes les copies au démarrage. À moins qu'il n'y ait d'autres idées là-bas?

Réponses:


269

En langage C, les objets avec une durée de stockage statique doivent être initialisés avec des expressions constantes ou avec des initialiseurs d'agrégat contenant des expressions constantes.

Un "grand" objet n'est jamais une expression constante en C, même si l'objet est déclaré comme const.

En outre, dans le langage C, le terme « constante » fait référence à des constantes littérales (comme 1, 'a', 0xFFet ainsi de suite), les membres de ENUM, et les résultats de ces opérateurs comme sizeof. Les objets qualifiés par const (de tout type) ne sont pas des constantes dans la terminologie du langage C. Ils ne peuvent pas être utilisés dans les initialiseurs d'objets avec une durée de stockage statique, quel que soit leur type.

Par exemple, ce n'est PAS une constante

const int N = 5; /* `N` is not a constant in C */

Ce qui précède Nserait une constante en C ++, mais ce n'est pas une constante en C. Donc, si vous essayez de faire

static int j = N; /* ERROR */

vous obtiendrez la même erreur: une tentative d'initialisation d'un objet statique avec une non-constante.

C'est la raison pour laquelle, en langage C, nous utilisons principalement #definepour déclarer des constantes nommées et #definepour créer des initialiseurs d'agrégats nommés.


2
+5 pour la belle explication, mais étonnamment, ce programme se compile bien sur ideone: ideone.com/lx4Xed . Est-ce un bogue de compilateur ou une extension de compilateur? Thanks
Destructor

2
@meet: Je ne sais pas quelle combinaison d'options de compilateur ideone utilise sous le capot, mais leurs résultats sont souvent étranges au-delà de toute description. J'ai essayé de compiler ce code sur Coliru ( coliru.stacked-crooked.com/a/daae3ce4035f5c8b ) et j'ai obtenu l'erreur attendue quel que soit le paramètre de dialecte du langage C que j'ai utilisé. Je ne vois rien de semblable sur le site Web de GCC en tant qu'extension de langage C. En d'autres termes, je n'ai aucune idée de comment et pourquoi il se compile en ideone. Même s'il se compile comme une extension de langage, il devrait quand même produire un message de diagnostic en C.
AnT

15
enum { N = 5 };est une manière sous-estimée de déclarer des constantes sans avoir à recourir à #define.
MM

2
@PravasiMeet "ideone" n'affiche tout simplement pas la plupart des messages de diagnostic produits par le compilateur, ce n'est donc pas un très bon site à utiliser pour déterminer si le code est correct ou non.
MM

1
J'ai découvert quelque chose d'intéressant. si ptr est un pointeur statique défini à l'intérieur d'une fonction, c'est une erreur: static int* ptr = malloc(sizeof(int)*5);mais ce n'est PAS une erreur static int* ptr; ptr = malloc(sizeof(int)*5);:: D
aderchox

74

C'est une limitation de la langue. Dans la section 6.7.8 / 4:

Toutes les expressions dans un initialiseur pour un objet qui a une durée de stockage statique doivent être des expressions constantes ou des littéraux de chaîne.

Dans la section 6.6, la spécification définit ce qui doit être considéré comme une expression constante. Nulle part il n'indique qu'une variable const doit être considérée comme une expression constante. Il est légal pour un compilateur d'étendre this ( 6.6/10 - An implementation may accept other forms of constant expressions) mais cela limiterait la portabilité.

Si vous pouvez changer my_foopour qu'il n'ait pas de stockage statique, tout ira bien:

int main()
{
    foo_t my_foo = foo_init;
    return 0;
}

J'aime le fait que vous ayez cité les spécifications, mais cela ne m'aide pas à comprendre ce que nous sommes censés faire ou pourquoi les choses sont telles qu'elles sont.
Evan Carroll

1
Il semble que GCC 8.1 (et plus tard) a implémenté une extension comme décrit dans cette réponse; il accepte static const int x = 3; static int y = x;.
Eric Postpischil le

5

Juste pour illustration par comparaison et contraste Le code provient de http://www.geeksforgeeks.org/g-fact-80/ / Le code échoue dans gcc et passe dans g ++ /

#include<stdio.h>
int initializer(void)
{
    return 50;
}

int main()
{
    int j;
    for (j=0;j<10;j++)
    {
        static int i = initializer();
        /*The variable i is only initialized to one*/
        printf(" value of i = %d ", i);
        i++;
    }
    return 0;
}

2

C'est un peu vieux, mais j'ai rencontré un problème similaire. Vous pouvez le faire si vous utilisez un pointeur:

#include <stdio.h>
typedef struct foo_t  {
    int a; int b; int c;
} foo_t;
static const foo_t s_FooInit = { .a=1, .b=2, .c=3 };
// or a pointer
static const foo_t *const s_pFooInit = (&(const foo_t){ .a=2, .b=4, .c=6 });
int main (int argc, char **argv) {
    const foo_t *const f1 = &s_FooInit;
    const foo_t *const f2 = s_pFooInit;
    printf("Foo1 = %d, %d, %d\n", f1->a, f1->b, f1->c);
    printf("Foo2 = %d, %d, %d\n", f2->a, f2->b, f2->c);
    return 0;
}

5
Je ne vois pas de variable avec une durée de stockage statique initialisée par une non constante ici.
Au revoir SE le

0

gcc 7.4.0 ne peut pas compiler les codes comme ci-dessous:

#include <stdio.h>
const char * const str1 = "str1";
const char * str2 = str1;
int main() {
    printf("%s - %s\n", str1, str2);
    return 0;
}

constchar.c: 3: 21: erreur: l'élément d'initialisation n'est pas constant const char * str2 = str1;

En fait, une chaîne "const char *" n'est pas une constante de compilation, elle ne peut donc pas être un initialiseur. Mais une chaîne "const char * const" est une constante à la compilation, elle devrait pouvoir être un initialiseur. Je pense que c'est un petit inconvénient de CLang.

Un nom de fonction est bien sûr une constante de compilation, donc ce code fonctionne:

void func(void)
{
    printf("func\n");
}
typedef void (*func_type)(void);
func_type f = func;
int main() {
    f();
    return 0;
}

Dans le code que vous avez posté, str1n'est pas une expression selon 6.7.9 Initialisation , paragraphe 4 : "Toutes les expressions dans un initialiseur pour un objet qui a une durée de stockage statique ou de thread doivent être des expressions constantes ou des chaînes littérales."
Andrew Henle le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.