web-dev-qa-db-fra.com

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

Je reçois 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;
}

N'oubliez pas qu'il s'agit d'une version simplifiée d'un projet multi-fichiers plus important 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. Puisqu'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érerais 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.

Est-ce que cela a à voir avec les ORG dans un fichier objet? Ces variables initialisées vont dans un ORG et sont initialisées en copiant le contenu d'un second ORG?

Peut-être devrais-je simplement changer de tactique et laisser une fonction d'initialisation faire toutes les copies au démarrage. À moins qu'il y ait d'autres idées là-bas?

170
tomlogic

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

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

De plus, en langage C, le terme "constante" désigne constantes littérales (comme 1, 'a', 0xFF et ainsi de suite), membres de l'énum et résultats de des opérateurs tels que sizeof. Les objets qualifiés de const (de tout type) sont pas des constantes dans la terminologie du langage C. Ils ne peuvent pas être utilisés dans les initialiseurs d'objets à durée de stockage statique, quel que soit leur type.

Par exemple, ceci est NOT une constante

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

Le N ci-dessus serait une constante en C++, mais ce n'est pas une constante en C. Donc, si vous essayez de le faire

static int j = N; /* ERROR */

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

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

244
AnT

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

Toutes les expressions dans un initialiseur pour un objet ayant 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. No where indique-t-il qu'une variable const doit être considérée comme une expression constante? Il est légal pour un compilateur d’étendre cela (6.6/10 - An implementation may accept other forms of constant expressions) mais cela limiterait la portabilité.

Si vous pouvez changer my_foo afin qu'il ne soit pas doté de stockage statique, vous pourrez continuer:

int main()
{
    foo_t my_foo = foo_init;
    return 0;
}
72
R Samuel Klatchko

Juste pour illustration en comparant et en contrastant Le code provient de http://www.geeksforgeeks.org/g-fact-80/ / Le code échoue dans gcc et passe en 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;
}
5
achoora

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;
}
2
valenumr