Fehler "Initialisierungselement ist nicht konstant" beim Versuch, eine Variable mit const zu initialisieren

186

Ich erhalte eine Fehlermeldung in Zeile 6 (initialisiere my_foo auf foo_init) des folgenden Programms und bin mir nicht sicher, warum.

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

Beachten Sie, dass dies eine vereinfachte Version eines größeren Projekts mit mehreren Dateien ist, an dem ich arbeite. Das Ziel war es, eine einzige Konstante in der Objektdatei zu haben, mit der mehrere Dateien eine Statusstruktur initialisieren können. Da es sich um ein eingebettetes Ziel mit begrenzten Ressourcen handelt und die Struktur nicht so klein ist, möchte ich nicht mehrere Kopien der Quelle. Ich würde es vorziehen, nicht zu verwenden:

#define foo_init { 1, 2, 3 }

Ich versuche auch, portablen Code zu schreiben, daher benötige ich eine Lösung, die C89 oder C99 gültig ist.

Hat dies mit den ORGs in einer Objektdatei zu tun? Dass initialisierte Variablen in eine ORG gehen und durch Kopieren des Inhalts einer zweiten ORG initialisiert werden?

Vielleicht muss ich nur meine Taktik ändern und alle Kopien beim Start von einer Initialisierungsfunktion ausführen lassen. Es sei denn, es gibt andere Ideen da draußen?

Tomlogik
quelle

Antworten:

269

In der Sprache C müssen Objekte mit statischer Speicherdauer mit konstanten Ausdrücken oder mit aggregierten Initialisierern mit konstanten Ausdrücken initialisiert werden.

Ein "großes" Objekt ist in C niemals ein konstanter Ausdruck, selbst wenn das Objekt als deklariert ist const.

Darüber hinaus ist in der Sprache C, bezieht sich der Begriff „konstant“ , um Literalkonstanten (wie 1, 'a', 0xFFund so weiter), ENUM - Mitglieder, und die Ergebnisse derartiger Operatoren als sizeof. Konstantenqualifizierte Objekte (jeglicher Art) sind keine Konstanten in der C-Sprachterminologie. Sie können unabhängig von ihrem Typ nicht in Initialisierern von Objekten mit statischer Speicherdauer verwendet werden.

Dies ist beispielsweise KEINE Konstante

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

Das Obige Nwäre eine Konstante in C ++, aber es ist keine Konstante in C. Wenn Sie es also versuchen

static int j = N; /* ERROR */

Sie erhalten den gleichen Fehler: einen Versuch, ein statisches Objekt mit einer Nichtkonstante zu initialisieren.

Dies ist der Grund, warum wir in der C-Sprache vorwiegend #definebenannte Konstanten deklarieren und #definebenannte aggregierte Initialisierer erstellen.

Ameise
quelle
2
+5 für die nette Erklärung, aber überraschenderweise lässt sich dieses Programm auf ideone gut kompilieren: ideone.com/lx4Xed . Ist es ein Compiler-Fehler oder eine Compiler-Erweiterung? Danke
Zerstörer
2
@meet: Ich weiß nicht, welche Kombination von Compiler-Optionen ideone unter der Haube verwendet, aber ihre Ergebnisse sind oft unbeschreiblich seltsam. Ich habe versucht, diesen Code in Coliru ( coliru.stacked-crooked.com/a/daae3ce4035f5c8b ) zu kompilieren, und habe den erwarteten Fehler dafür erhalten, unabhängig davon, welche Dialekteinstellung in C ich verwendet habe. Ich sehe so etwas nicht auf der GCC-Website als C-Spracherweiterung. Mit anderen Worten, ich habe keine Ahnung, wie und warum es in ideone kompiliert wird. Selbst wenn es als Spracherweiterung kompiliert wird, sollte es dennoch eine Diagnosemeldung in C erzeugen.
AnT
15
enum { N = 5 };ist eine unterschätzte Methode, Konstanten zu deklarieren, ohne darauf zurückgreifen zu müssen #define.
MM
2
@PravasiMeet "ideone" zeigt einfach nicht viele der vom Compiler erzeugten Diagnosemeldungen an, daher ist es keine sehr gute Site, um festzustellen, ob der Code korrekt ist oder nicht.
MM
1
Ich habe etwas Interessantes herausgefunden. Wenn ptr ein statischer Zeiger ist, der innerhalb einer Funktion definiert ist, ist dies ein Fehler: static int* ptr = malloc(sizeof(int)*5);aber dies ist KEIN Fehler static int* ptr; ptr = malloc(sizeof(int)*5);
::
74

Es ist eine Einschränkung der Sprache. In Abschnitt 6.7.8 / 4:

Alle Ausdrücke in einem Initialisierer für ein Objekt mit statischer Speicherdauer müssen konstante Ausdrücke oder Zeichenfolgenliterale sein.

In Abschnitt 6.6 definiert die Spezifikation, was als konstanter Ausdruck betrachtet werden muss. Nein, wo steht, dass eine const-Variable als konstanter Ausdruck betrachtet werden muss. Es ist für einen Compiler legal, this ( 6.6/10 - An implementation may accept other forms of constant expressions) zu erweitern, dies würde jedoch die Portabilität einschränken.

Wenn Sie Änderungen vornehmen können, my_foodamit kein statischer Speicher vorhanden ist, sind Sie in Ordnung:

int main()
{
    foo_t my_foo = foo_init;
    return 0;
}
R Samuel Klatchko
quelle
Ich mag es, dass Sie die Spezifikation zitiert haben, aber das hilft mir nicht zu verstehen, was wir tun sollen oder warum die Dinge so sind, wie sie sind.
Evan Carroll
1
Es scheint, dass GCC 8.1 (und höher) eine Erweiterung implementiert hat, wie in dieser Antwort beschrieben. es akzeptiert static const int x = 3; static int y = x;.
Eric Postpischil
5

Nur zur Veranschaulichung durch Vergleich und Kontrast Der Code stammt von http://www.geeksforgeeks.org/g-fact-80/ / Der Code schlägt in gcc fehl und wird in g ++ / übergeben

#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
quelle
2

Das ist ein bisschen alt, aber ich bin auf ein ähnliches Problem gestoßen. Sie können dies tun, wenn Sie einen Zeiger verwenden:

#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
quelle
5
Ich sehe keine Variable mit statischer Speicherdauer, die hier durch eine Nichtkonstante initialisiert wird.
Auf Wiedersehen SE
0

gcc 7.4.0 kann keine Codes wie folgt kompilieren:

#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: Fehler: Initialisiererelement ist nicht konstant const char * str2 = str1;

Tatsächlich ist eine Zeichenfolge "const char *" keine Konstante zur Kompilierungszeit, daher kann sie kein Initialisierer sein. Ein "const char * const" -String ist jedoch eine Konstante zur Kompilierungszeit. Er sollte ein Initialisierer sein können. Ich denke, das ist ein kleiner Nachteil von CLang.

Ein Funktionsname ist natürlich eine Konstante zur Kompilierungszeit. Dieser Code funktioniert also:

void func(void)
{
    printf("func\n");
}
typedef void (*func_type)(void);
func_type f = func;
int main() {
    f();
    return 0;
}
xjtuecho
quelle
In dem von Ihnen veröffentlichten Code str1ist kein Ausdruck gemäß 6.7.9 Initialisierung , Absatz 4 : "Alle Ausdrücke in einem Initialisierer für ein Objekt mit statischer oder Thread-Speicherdauer müssen konstante Ausdrücke oder Zeichenfolgenliterale sein."
Andrew Henle