constで変数を初期化しようとすると、「initializer element is not constant」エラーが発生する


186

次のプログラムの6行目(my_fooをfoo_initに初期化)でエラーが発生しましたが、その理由がわかりません。

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

これは、私が取り組んでいる大規模なマルチファイルプロジェクトの簡易バージョンです。目標は、オブジェクトファイルに単一の定数を含めることで、複数のファイルが状態構造を初期化するために使用できます。それは限られたリソースを備えた組み込みターゲットであり、構造体はそれほど小さくないので、ソースの複数のコピーは必要ありません。私は使用したくない:

#define foo_init { 1, 2, 3 }

また、移植可能なコードを記述しようとしているので、有効なC89またはC99のソリューションが必要です。

これはオブジェクトファイル内のORGに関係していますか?その初期化された変数は1つのORGに入り、2番目のORGの内容をコピーすることによって初期化されますか?

多分私は私の戦術を変更する必要があり、起動時にすべてのコピーを実行する初期化関数を持っている必要があります。そこに他のアイデアがない限り?

回答:


269

C言語では、静的ストレージ期間を持つオブジェクトは、定数式、または定数式を含む集約初期化子で初期化する必要があります。

「大きな」オブジェクトは、オブジェクトがとして宣言されていても、Cでは定数式にはなりませんconst

また、C言語では、用語「定数」とは、リテラル定数(のような1'a'0xFFなど)、列挙体、およびこのような演算子の結果sizeof。(任意のタイプの)const修飾オブジェクトは、C言語の用語では定数ではありません。タイプに関係なく、静的ストレージ期間を持つオブジェクトの初期化子では使用できません。

たとえば、これは定数ではありません

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

上記NはC ++では定数になりますが、Cでは定数ではありません。

static int j = N; /* ERROR */

同じエラーが発生します:静的オブジェクトを非定数で初期化しようとしました。

これが、C言語で主に#define名前付き定数の宣言に使用し、#define名前付き集約初期化子の作成に頼る理由です。


2
良い説明のために+5ですが、驚くべきことに、このプログラムはideone:ideone.com/lx4Xedで正常にコンパイルされます。コンパイラのバグですか、それともコンパイラの拡張ですか?ありがとう
デストラクタ

2
@meet:ideoneが内部で使用しているコンパイラオプションの組み合わせはわかりませんが、その結果は説明を超えて奇妙なことがよくあります。私はこのコードをColiru(coliru.stacked-crooked.com/a/daae3ce4035f5c8b)でコンパイルしてみましたが、使用したC言語の方言設定に関係なく、予想されるエラーが発生しました。GCCのWebサイトにC言語拡張としてリストされているようなものはありません。つまり、ideoneでコンパイルする方法と理由がわかりません。これは言語拡張としてコンパイルする場合であっても、それはまだCの診断メッセージ生成すべきである
ANT

15
enum { N = 5 };を使用せずに定数を宣言する、あまり評価されていない方法です#define
MM

2
@PravasiMeet "ideone"は、コンパイラが生成する診断メッセージの多くを表示しないので、コードが正しいかどうかを判断するのに使用するのに適したサイトではありません。
MM

1
面白いことがわかりました。ptrが関数内で定義された静的ポインターである場合、これはエラーです。static int* ptr = malloc(sizeof(int)*5);ただし、これはエラーではありませんstatic int* ptr; ptr = malloc(sizeof(int)*5);::D
aderchox

74

言語の制限です。セクション6.7.8 / 4で:

静的な保存期間を持つオブジェクトの初期化子のすべての式は、定数式または文字列リテラルでなければなりません。

セクション6.6の仕様では、定数式と見なす必要があるものを定義しています。const変数を定数式と見なす必要があるとはどこにも記載されていません。コンパイラがこれを拡張することは合法です(6.6/10 - An implementation may accept other forms of constant expressions)が、これは移植性を制限します。

my_foo静的ストレージがないように変更できる場合は、問題ありません。

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

私はあなたが仕様を引用したのが好きですが、これは私たちが何をすることになっているのか、なぜ物事が彼らのやり方であるのかを理解するのに役立ちません。
エヴァンキャロル

1
GCC 8.1(およびそれ以降)は、この回答で説明されているようにいくつかの拡張機能を実装しているようです。受け入れますstatic const int x = 3; static int y = x;
Eric Postpischil

5

単に比較と対比による説明のためにコードはhttp://www.geeksforgeeks.org/g-fact-80/からのものです / コードはgccで失敗し、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

これは少し古いですが、同様の問題に遭遇しました。ポインターを使用する場合、これを行うことができます。

#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
ここでは、非定数によって初期化される静的ストレージ期間を持つ変数が表示されません。
Goodbye SE

0

gcc 7.4.0は以下のようにコードをコンパイルできません。

#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:エラー:初期化子要素が定数ではありませんconst char * str2 = str1;

実際、「const char *」文字列はコンパイル時の定数ではないため、初期化子にすることはできません。ただし、「const char * const」文字列はコンパイル時の定数であり、初期化子にできるはずです。これはCLangの小さな欠点だと思います。

関数名はもちろんコンパイル時の定数なので、このコードは機能します:

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

あなたが投稿のコードでは、str1ない表現あたり6.7.9初期化、第4項:「初期化子内のすべての式の静的またはスレッド記憶域期間を持つオブジェクトのためには、定数式または文字列リテラルでなければなりません。」
Andrew Henle
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.