Fehler „Initialisierungselement ist nicht konstant“ beim Versuch, Variable mit const zu initialisieren

Lesezeit: 7 Minuten

Benutzeravatar von tomlogic
tomlogik

Ich erhalte einen Fehler in Zeile 6 (initialize my_foo to foo_init) des folgenden Programms und ich bin mir nicht sicher, ob ich den Grund verstehe.

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

Denken Sie daran, 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, die mehrere Dateien verwenden können, um eine Zustandsstruktur zu initialisieren. 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, also brauche ich eine Lösung, die gültiges C89 oder C99 ist.

Hat das 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 eine Initialisierungsfunktion alle Kopien beim Start ausführen lassen. Es sei denn, es gibt noch andere Ideen?

  • Behoben in gcc-8.1 und höher, siehe die Antwort von @Zaman unten für Details.

    – Bruce

    1. Oktober 2021 um 8:07 Uhr

AnT steht mit Russlands Benutzer-Avatar
AnT steht zu Russland

In der C-Sprache müssen Objekte mit statischer Speicherdauer mit initialisiert werden konstante Ausdrückeoder mit Aggregatinitialisierern, die konstante Ausdrücke enthalten.

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

Darüber hinaus bezieht sich der Begriff “Konstante” in der C-Sprache auf wörtliche Konstanten (wie 1, 'a', 0xFF und so weiter), Aufzählungsmitglieder und Ergebnisse solcher Operatoren wie sizeof. Const-qualifizierte Objekte (jeglicher Art) sind keine Konstanten in der Terminologie der C-Sprache. Sie können unabhängig von ihrem Typ nicht in Initialisierern von Objekten mit statischer Speicherdauer verwendet werden.

Dies ist zum Beispiel NICHT eine Konstante

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

Obenstehendes N wäre eine Konstante in C++, aber es ist keine Konstante in C. Also, wenn Sie es versuchen

static int j = N; /* ERROR */

Sie erhalten denselben Fehler: ein Versuch, ein statisches Objekt mit einer Nichtkonstanten zu initialisieren.

Aus diesem Grund verwenden wir in der C-Sprache überwiegend #define benannte Konstanten zu deklarieren und auch darauf zurückzugreifen #define um benannte Aggregatinitialisierer zu erstellen.

  • +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-Bug oder eine Compiler-Erweiterung? Vielen Dank

    – Destruktor

    21. Juni 2015 um 6:25 Uhr

  • @meet: Ich weiß nicht, welche Kombination von Compileroptionen ideone unter der Haube verwendet, aber ihre Ergebnisse sind oft unbeschreiblich seltsam. Ich habe versucht, diesen Code auf Coliru zu kompilieren (coliru.stacked-crooked.com/a/daae3ce4035f5c8b) und bekam den erwarteten Fehler dafür, unabhängig davon, welche C-Sprachdialekteinstellung ich verwendet habe. Ich sehe nichts dergleichen, das auf der GCC-Website als C-Spracherweiterung aufgeführt ist. Mit anderen Worten, ich habe keine Ahnung, wie und warum es in ideone kompiliert wird. Auch wenn es als Spracherweiterung kompiliert wird, sollte es dennoch eine Diagnosemeldung in C erzeugen.

    – AnT steht zu Russland

    21. Juni 2015 um 6:38 Uhr


  • enum { N = 5 }; ist eine unterschätzte Art, Konstanten zu deklarieren, ohne darauf zurückgreifen zu müssen #define.

    – MM

    10. Dezember 2015 um 2:51 Uhr


  • @PravasiMeet “ideone” zeigt einfach nicht viele der Diagnosemeldungen an, die der Compiler erzeugt, daher ist es keine sehr gute Website, um festzustellen, ob der Code korrekt ist oder nicht.

    – MM

    10. Dezember 2015 um 2:51 Uhr

  • Ich habe etwas Interessantes herausgefunden. Wenn ptr ein statischer Zeiger ist, der in einer Funktion definiert ist, ist dies ein Fehler: static int* ptr = malloc(sizeof(int)*5); aber das ist KEIN Fehler: static int* ptr; ptr = malloc(sizeof(int)*5); 😀

    – aderchox

    5. Januar 2019 um 19:33 Uhr

Benutzeravatar von R Samuel Klatchko
R. Samuel Klatschko

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 angesehen werden muss. An keiner Stelle steht, dass eine konstante Variable als konstanter Ausdruck betrachtet werden muss. Es ist für einen Compiler legal, dies zu erweitern (6.6/10 - An implementation may accept other forms of constant expressions), aber das würde die Portabilität einschränken.

Wenn du dich ändern kannst my_foo Es hat also keinen statischen Speicher, Sie wären in Ordnung:

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

  • Mir gefällt, 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 Caroll

    23. August 2018 um 22:30 Uhr

  • 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

    17. Februar 2020 um 16:24 Uhr


2021: Denn wer erreicht diesen Posten wegen arm-none-eabi-gcc.exe Kompilierungsfehler auf STM32-MCUs:
Ändern Sie Ihre Toolchain auf gnu-tools-for-stm32.9-2020-q2-update.

Ab GCC V8.1+ wird der Initialisierer für verschachtelte Konstanten unterstützt und der folgende Code wird kompiliert.

const int a = 1;
const int b = a +1;

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

arm-none-eabi-gcc.exe in gnu-tools-for-stm32.7-2018-q2-update basiert auf gcc v7.3.1 und der obige Code wird nicht kompiliert! Aber gnu-tools-for-stm32.9-2020-q2-update Verwendet gcc v9.3.1 und wird kompilieren.

Weitere Informationen finden Sie unter:
Warum “Initialisierungselement ist keine Konstante” funktioniert nicht mehr?
und
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69960#c18

Benutzeravatar von achoora
Achora

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 geht in g++ über/

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

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

  • Ich sehe hier keine Variable mit statischer Speicherdauer, die durch eine Nichtkonstante initialisiert wird.

    – Kami Kaze

    9. Februar 2017 um 10:29 Uhr

gcc 7.4.0 kann folgende Codes nicht 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: Initialisierungselement ist nicht konstant const char * str2 = str1;

Tatsächlich ist ein “const char *”-String keine Konstante zur Kompilierzeit, also kann er kein Initialisierer sein. Aber eine “const char * const”-Zeichenfolge ist eine Konstante zur Kompilierzeit, sie sollte in der Lage sein, ein Initialisierer zu sein. Ich denke, das ist ein kleiner Nachteil von CLang.

Ein Funktionsname ist natürlich eine Kompilierzeitkonstante. Dieser Code funktioniert also:

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

  • Ich sehe hier keine Variable mit statischer Speicherdauer, die durch eine Nichtkonstante initialisiert wird.

    – Kami Kaze

    9. Februar 2017 um 10:29 Uhr

Ich hatte diesen Fehler im Code, der so aussah:

int A = 1;
int B = A;

Die Lösung besteht darin, es so zu ändern

int A = 1;
#define B A

Der Compiler weist einer Variablen einen Speicherort zu. Die zweite versucht, eine zweite Variable an derselben Stelle wie die erste zuzuweisen – was keinen Sinn macht. Die Verwendung des Makro-Präprozessors löst das Problem.

  • Es ist völlig normal, den Wert einer Variablen einer anderen zuzuweisen. Sie sagen, dass ein Speicherplatz zugewiesen wird, aber es gibt hier keine Zeiger (wäre anders, wenn Sie es getan hätten). int* oder &A), es sei denn, Sie speichern Zeiger als ints (also die 1 gespeichert in A bezieht sich auf eine Speicheradresse), was ein ganz anderes Thema ist.

    – Jacobq

    9. Dezember 2021 um 16:35 Uhr


1426020cookie-checkFehler „Initialisierungselement ist nicht konstant“ beim Versuch, Variable mit const zu initialisieren

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy