Errore "elemento inizializzatore non costante" quando si tenta di inizializzare la variabile con const


187

Viene visualizzato un errore sulla riga 6 (inizializza my_foo su foo_init) del seguente programma e non sono sicuro di capire il perché.

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

Tieni presente che questa è una versione semplificata di un progetto più ampio e multi-file su cui sto lavorando. L'obiettivo era di avere una singola costante nel file oggetto, che più file potessero utilizzare per inizializzare una struttura di stato. Dal momento che è un obiettivo incorporato con risorse limitate e la struttura non è così piccola, non voglio più copie della fonte. Preferirei non usare:

#define foo_init { 1, 2, 3 }

Sto anche cercando di scrivere codice portatile, quindi ho bisogno di una soluzione valida C89 o C99.

Questo ha a che fare con gli ORG in un file oggetto? Che le variabili inizializzate vanno in un ORG e vengono inizializzate copiando il contenuto di un secondo ORG?

Forse dovrò solo cambiare la mia tattica e avere una funzione di inizializzazione per fare tutte le copie all'avvio. A meno che non ci siano altre idee là fuori?

Risposte:


269

Nel linguaggio C, gli oggetti con durata di memorizzazione statica devono essere inizializzati con espressioni costanti o con inizializzatori aggregati contenenti espressioni costanti.

Un oggetto "grande" non è mai un'espressione costante in C, anche se l'oggetto è dichiarato come const.

Inoltre, in linguaggio C, il termine "costante" si riferisce a costanti letterali (come 1, 'a', 0xFFe così via), membri enum, ei risultati di tali operatori come sizeof. Gli oggetti con qualifica costante (di qualsiasi tipo) non sono costanti nella terminologia del linguaggio C. Non possono essere utilizzati negli inizializzatori di oggetti con durata di memorizzazione statica, indipendentemente dal loro tipo.

Ad esempio, questa NON è una costante

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

Quanto sopra Nsarebbe una costante in C ++, ma non è una costante in C. Quindi, se provi a farlo

static int j = N; /* ERROR */

otterrai lo stesso errore: un tentativo di inizializzare un oggetto statico con una non costante.

Questo è il motivo per cui, in linguaggio C, usiamo prevalentemente #defineper dichiarare costanti nominate e ricorrere anche #definealla creazione di inizializzatori aggregati denominati.


2
+5 per la bella spiegazione, ma sorprendentemente questo programma si compila bene su ideone: ideone.com/lx4Xed . È un bug del compilatore o un'estensione del compilatore? Grazie
Destructor il

2
@meet: non so quale combinazione di opzioni di compilatore viene utilizzata da ideone, ma i loro risultati sono spesso strani oltre ogni descrizione. Ho provato a compilare questo codice su Coliru ( coliru.stacked-crooked.com/a/daae3ce4035f5c8b ) e ho ottenuto l'errore previsto per esso indipendentemente dall'impostazione del dialetto in linguaggio C che ho usato. Non vedo nulla di simile elencato sul sito web di GCC come estensione in linguaggio C. In altre parole, non ho idea di come e perché si compili in ideone. Anche se si compila come estensione del linguaggio, dovrebbe comunque produrre un messaggio diagnostico in C.
AnT

15
enum { N = 5 };è un modo poco apprezzato per dichiarare le costanti senza dover ricorrere a #define.
MM

2
@PravasiMeet "ideone" semplicemente non mostra molti dei messaggi diagnostici prodotti dal compilatore, quindi non è un sito molto buono da usare per determinare se il codice è corretto o meno.
MM

1
Ho scoperto qualcosa di interessante. se ptr è un puntatore statico definito all'interno di una funzione, questo è un errore: static int* ptr = malloc(sizeof(int)*5);ma questo NON è un errore static int* ptr; ptr = malloc(sizeof(int)*5);:: D
aderchox

74

È una limitazione della lingua. Nella sezione 6.7.8 / 4:

Tutte le espressioni in un inizializzatore per un oggetto che ha una durata di memorizzazione statica devono essere espressioni costanti o valori letterali di stringa.

Nella sezione 6.6, la specifica definisce ciò che deve essere considerato un'espressione costante. Da nessuna parte si afferma che una variabile const deve essere considerata un'espressione costante. È legale per un compilatore estendere questo ( 6.6/10 - An implementation may accept other forms of constant expressions) ma ciò limiterebbe la portabilità.

Se è possibile modificare in my_foomodo che non abbia memoria statica, andrebbe bene:

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

Mi piace che tu abbia citato le specifiche, ma questo non mi aiuta a capire cosa dovremmo fare o perché le cose siano come sono.
Evan Carroll,

1
Sembra che GCC 8.1 (e successivi) abbia implementato alcune estensioni come descritto in questa risposta; accetta static const int x = 3; static int y = x;.
Eric Postpischil,

5

Solo per illustrazione per confronto e contrasto Il codice è tratto da http://www.geeksforgeeks.org/g-fact-80/ / Il codice ha esito negativo in gcc e passa in 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

Questo è un po 'vecchio, ma ho riscontrato un problema simile. Puoi farlo se usi un puntatore:

#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
Non vedo una variabile con durata dell'archiviazione statica inizializzata da una non costante qui.
Arrivederci SE

0

gcc 7.4.0 non può compilare codici come di seguito:

#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: errore: l'elemento di inizializzazione non è costante const char * str2 = str1;

In effetti, una stringa "const char *" non è una costante di compilazione, quindi non può essere un inizializzatore. Ma una stringa "const char * const" è una costante di compilazione, dovrebbe essere in grado di essere un inizializzatore. Penso che questo sia un piccolo inconveniente di CLang.

Il nome di una funzione è ovviamente una costante di compilazione, quindi questo codice funziona:

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

Nel codice che hai pubblicato, str1non è un'espressione per 6.7.9 Inizializzazione , paragrafo 4 : "Tutte le espressioni in un inizializzatore per un oggetto che ha una durata di memorizzazione statica o di thread devono essere espressioni costanti o valori letterali di stringa".
Andrew Henle,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.