Kesalahan "elemen penginisialisasi tidak konstan" ketika mencoba menginisialisasi variabel dengan const

187

Saya mendapatkan kesalahan pada baris 6 (inisialisasi my_foo ke foo_init) dari program berikut dan saya tidak yakin saya mengerti mengapa.

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

Ingatlah ini adalah versi sederhana dari proyek multi-file yang lebih besar yang saya kerjakan. Tujuannya adalah untuk memiliki konstanta tunggal dalam file objek, yang dapat digunakan beberapa file untuk menginisialisasi struktur keadaan. Karena ini merupakan target yang disematkan dengan sumber daya terbatas dan struct tidak terlalu kecil, saya tidak ingin banyak salinan sumber. Saya lebih suka tidak menggunakan:

#define foo_init { 1, 2, 3 }

Saya juga mencoba menulis kode portabel, jadi saya butuh solusi yang valid C89 atau C99.

Apakah ini ada hubungannya dengan ORG dalam file objek? Variabel yang diinisialisasi masuk ke dalam satu ORG dan diinisialisasi dengan menyalin isi ORG kedua?

Mungkin saya hanya perlu mengubah taktik saya, dan memiliki fungsi inisialisasi melakukan semua salinan saat startup. Kecuali ada ide lain di luar sana?

tomlogic
sumber

Jawaban:

269

Dalam bahasa C, objek dengan durasi penyimpanan statis harus diinisialisasi dengan ekspresi konstan , atau dengan inisialisasi agregat yang berisi ekspresi konstan.

Objek "besar" tidak pernah merupakan ekspresi konstan dalam C, bahkan jika objek tersebut dinyatakan sebagai const.

Selain itu, dalam bahasa C, istilah "konstan" mengacu pada konstanta literal (seperti 1, 'a', 0xFFdan sebagainya), anggota enum, dan hasil dari operator seperti sizeof. Objek dengan kualifikasi konstan (jenis apa pun) bukan konstanta dalam terminologi bahasa C. Mereka tidak dapat digunakan dalam inisialisasi objek dengan durasi penyimpanan statis, terlepas dari jenisnya.

Sebagai contoh, ini BUKAN konstanta

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

Di atas Nakan menjadi konstanta di C ++, tetapi itu bukan konstanta di C. Jadi, jika Anda mencoba melakukannya

static int j = N; /* ERROR */

Anda akan mendapatkan kesalahan yang sama: upaya untuk menginisialisasi objek statis dengan non-konstan.

Ini adalah alasan mengapa, dalam bahasa C, kami sebagian besar menggunakan #defineuntuk mendeklarasikan konstanta bernama, dan juga menggunakan #defineuntuk membuat initializers agregat bernama.

Semut
sumber
2
5 untuk penjelasan yang bagus, tapi mengejutkan program ini mengkompilasi baik pada ideone: ideone.com/lx4Xed . Apakah itu kompiler bug atau ekstensi kompiler? Terima kasih
Destructor
2
@meet: Saya tidak tahu kombinasi apa yang digunakan ideone opsi kompiler di bawah tenda, tetapi hasilnya seringkali aneh di luar deskripsi. Saya mencoba mengkompilasi kode ini di Coliru ( coliru.stacked-crooked.com/a/daae3ce4035f5c8b ) dan mendapatkan kesalahan yang diharapkan untuk itu terlepas dari pengaturan dialek bahasa C apa yang saya gunakan. Saya tidak melihat hal seperti itu yang tercantum di situs web GCC sebagai ekstensi bahasa C. Dengan kata lain, saya tidak tahu bagaimana dan mengapa itu dikompilasi dalam ideone. Bahkan jika itu dikompilasi sebagai ekstensi bahasa, itu masih harus menghasilkan pesan diagnostik dalam C.
AnT
15
enum { N = 5 };adalah cara yang tidak dihargai untuk mendeklarasikan konstanta tanpa harus menggunakan #define.
MM
2
@PravasiMeet "ideone" sama sekali tidak menampilkan banyak pesan diagnostik yang dihasilkan oleh kompiler, jadi ini bukan situs yang sangat baik untuk digunakan untuk menentukan apakah kode itu benar atau tidak.
MM
1
Saya menemukan sesuatu yang menarik. jika ptr adalah pointer statis yang didefinisikan di dalam suatu fungsi, ini adalah kesalahan: static int* ptr = malloc(sizeof(int)*5);tapi ini BUKAN kesalahan static int* ptr; ptr = malloc(sizeof(int)*5);:: D
aderchox
74

Ini adalah batasan bahasa. Di bagian 6.7.8 / 4:

Semua ekspresi dalam penginisialisasi untuk objek yang memiliki durasi penyimpanan statis harus berupa ekspresi konstan atau string literal.

Di bagian 6.6, spesifikasi mendefinisikan apa yang harus dianggap sebagai ekspresi konstan. Tidak ada tempat yang menyatakan bahwa variabel const harus dianggap sebagai ekspresi konstan. Adalah legal bagi kompiler untuk memperpanjang ini ( 6.6/10 - An implementation may accept other forms of constant expressions) tetapi itu akan membatasi portabilitas.

Jika Anda dapat mengubahnya my_foosehingga tidak memiliki penyimpanan statis, Anda akan baik-baik saja:

int main()
{
    foo_t my_foo = foo_init;
    return 0;
}
R Samuel Klatchko
sumber
Saya suka bahwa Anda mengutip spec, tetapi ini tidak membantu saya memahami apa yang seharusnya kita lakukan atau mengapa semuanya seperti itu.
Evan Carroll
1
Tampaknya GCC 8.1 (dan lebih baru) telah menerapkan beberapa ekstensi seperti yang dijelaskan dalam jawaban ini; itu menerima static const int x = 3; static int y = x;.
Eric Postpischil
5

Hanya untuk ilustrasi dengan membandingkan dan membandingkan Kode ini berasal dari http://www.geeksforgeeks.org/g-fact-80/ / Kode gagal dalam gcc dan lulus di 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
sumber
2

Ini agak lama, tetapi saya mengalami masalah yang sama. Anda dapat melakukan ini jika menggunakan pointer:

#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
sumber
5
Saya tidak melihat variabel dengan durasi penyimpanan statis yang diinisialisasi oleh non-konstan di sini.
Selamat tinggal
0

gcc 7.4.0 tidak dapat mengkompilasi kode seperti di bawah ini:

#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: error: elemen penginisialisasi tidak konstan const char * str2 = str1;

Sebenarnya, string "const char *" bukan konstanta waktu kompilasi, jadi itu tidak bisa menjadi inisialisasi. Tetapi string "const char * const" adalah konstanta waktu kompilasi, ia harus dapat menjadi inisialisasi. Saya pikir ini adalah kelemahan kecil Dentang.

Nama fungsi tentu saja merupakan konstanta waktu kompilasi. Jadi kode ini berfungsi:

void func(void)
{
    printf("func\n");
}
typedef void (*func_type)(void);
func_type f = func;
int main() {
    f();
    return 0;
}
xjtuecho
sumber
Dalam kode yang Anda posting, str1bukan ekspresi per 6.7.9 Inisialisasi , paragraf 4 : "Semua ekspresi dalam penginisialisasi untuk objek yang memiliki durasi penyimpanan statis atau thread harus ekspresi konstan atau string literal."
Andrew Henle