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í?

tomlogic
fonte

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.

Formiga
fonte
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;
}
R Samuel Klatchko
fonte
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;
}
achoora
fonte
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;
}
valenumr
fonte
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;
}
xjtuecho
fonte
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