O erro "elemento inicializador não é constante" ao tentar inicializar a variável com const


187

Eu recebo um erro na linha 6 (inicialize my_foo para foo_init) do programa a seguir e não sei se entendi o porquê.

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

Lembre-se de que esta é uma versão simplificada de um projeto maior com vários arquivos em que estou trabalhando. O objetivo era ter uma única constante no arquivo de objeto, que vários arquivos pudessem usar para inicializar uma estrutura de estado. Como é um destino incorporado com recursos limitados e a estrutura não é tão pequena, não quero várias cópias da fonte. Prefiro não usar:

#define foo_init { 1, 2, 3 }

Também estou tentando escrever código portátil, por isso preciso de uma solução que seja válida C89 ou C99.

Isso tem a ver com os ORGs em um arquivo de objeto? Que variáveis ​​inicializadas entram em um ORG e são inicializadas copiando o conteúdo de um segundo ORG?

Talvez eu precise mudar minha tática e ter uma função de inicialização para fazer todas as cópias na inicialização. A menos que haja outras idéias por aí?

Respostas:


269

Na linguagem C, objetos com duração de armazenamento estático precisam ser inicializados com expressões constantes ou com inicializadores agregados contendo expressões constantes.

Um objeto "grande" nunca é uma expressão constante em C, mesmo que o objeto seja declarado como const.

Além disso, em linguagem C, o termo "constante" refere-se a constantes literais (como 1, 'a', 0xFFe assim por diante), enum membros, e os resultados de tais operadores como sizeof. Objetos qualificados de Const (de qualquer tipo) não são constantes na terminologia da linguagem C. Eles não podem ser usados ​​em inicializadores de objetos com duração de armazenamento estático, independentemente de seu tipo.

Por exemplo, isso NÃO é uma constante

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

O acima Nseria uma constante em C ++, mas não é uma constante em C. Portanto, se você tentar fazer

static int j = N; /* ERROR */

você receberá o mesmo erro: uma tentativa de inicializar um objeto estático com um não constante.

Essa é a razão pela qual, na linguagem C, usamos predominantemente #definepara declarar constantes nomeadas e também recorremos #defineà criação de inicializadores agregados nomeados.


2
+5 para uma boa explicação, mas, surpreendentemente, este programa compila perfeitamente o ideone: ideone.com/lx4Xed . É bug do compilador ou extensão do compilador? Obrigado
Destructor

2
@ meet: Eu não sei qual combinação de opções de compilador a ideone usa nos bastidores, mas seus resultados geralmente são estranhos além da descrição. Tentei compilar esse código no Coliru ( coliru.stacked-crooked.com/a/daae3ce4035f5c8b ) e obtive o erro esperado, independentemente da configuração de dialeto do idioma C. Não vejo nada parecido com o listado no site do GCC como uma extensão da linguagem C. Em outras palavras, não tenho idéia de como e por que compila em ideona. Mesmo que seja compilado como uma extensão de idioma, ainda deve produzir uma mensagem de diagnóstico em C.
AnT

15
enum { N = 5 };é uma maneira subestimada de declarar constantes sem ter que recorrer #define.
MM

2
O "ideone" do @PravasiMeet simplesmente não exibe muitas das mensagens de diagnóstico que o compilador produz, portanto, não é um site muito bom para determinar se o código está correto ou não.
MM

1
Eu descobri algo interessante. se ptr é um ponteiro estático definido dentro de uma função, isso é um erro: static int* ptr = malloc(sizeof(int)*5);mas NÃO é um erro static int* ptr; ptr = malloc(sizeof(int)*5);:: D
aderchox

74

É uma limitação do idioma. Na seção 6.7.8 / 4:

Todas as expressões em um inicializador para um objeto que tenha duração de armazenamento estático devem ser expressões constantes ou literais de cadeia de caracteres.

Na seção 6.6, a especificação define o que deve ser considerado uma expressão constante. Em nenhum lugar onde afirma que uma variável const deve ser considerada uma expressão constante. É legal para um compilador estender isso ( 6.6/10 - An implementation may accept other forms of constant expressions), mas isso limitaria a portabilidade.

Se você pode alterar my_foopara que ele não tenha armazenamento estático, você pode ficar bem:

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

Gosto que você tenha citado as especificações, mas isso não me ajuda a entender o que devemos fazer ou por que as coisas são do jeito que são.
Evan Carroll

1
Parece que o GCC 8.1 (e posterior) implementou alguma extensão, conforme descrito nesta resposta; aceita static const int x = 3; static int y = x;.
Eric Postpischil 17/02

5

Apenas para ilustração, compare e compare O código é de http://www.geeksforgeeks.org/g-fact-80/ / O código falha no gcc e passa no 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

Isso é um pouco antigo, mas tive um problema semelhante. Você pode fazer isso se usar um ponteiro:

#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
Não vejo uma variável com duração de armazenamento estático que seja inicializada por uma não constante aqui.
Adeus SE

0

O gcc 7.4.0 não pode compilar códigos como abaixo:

#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: erro: o elemento inicializador não é constante const char * str2 = str1;

De fato, uma string "const char *" não é uma constante em tempo de compilação, portanto, não pode ser um inicializador. Mas uma string "const char * const" é uma constante em tempo de compilação; deve poder ser um inicializador. Eu acho que isso é uma pequena desvantagem do CLang.

É claro que um nome de função é uma constante em tempo de compilação.

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

No código que você postou, str1não é uma expressão de acordo com 6.7.9 Inicialização , parágrafo 4 : "Todas as expressões em um inicializador para um objeto com duração de armazenamento estático ou de encadeamento devem ser expressões constantes ou literais de string".
Andrew Henle
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.