web-dev-qa-db-ja.com

const変数を使用してCの配列のサイズを宣言できますか?

次のコードがエラーをスローするのはなぜですか?

const int a = 5;
int b[a]={1,2,3,4,5};

また、「const」キーワードなしで上記のコードをコンパイルしようとすると、同じエラーが発生しました。

int a = 5; 
int b[a]={1,2,3,4,5};

なぜそうですか?ここで行っている間違いは何ですか?

また、別の質問:コード内で定数が実際の値で置き換えられるのはいつか、つまり変数を宣言する場合は次のようになります:const int x = 5;変数xにRAMにメモリが割り当てられていないが、ROMの値が5であり、xが単にコードのxのすべての場所に値5がありますが、これはいつ発生しますか?コンパイル時間?起動時間?前処理時間?

PS:私は、デスクトップで実行されているCではなく、Embedded C(マイクロコントローラーなどで実行されている)について話しています。したがって、組み込みシステムにはROM(フラッシュ、EEPROM ...)が必要です。その場合はどうなりますか?

35
user1901196

これは単に言語の制限です。静的にバインドされた配列のサイズは定数式である必要があり、残念ながらCではリテラル定数やsizeof式などのようなものだけですが、notconst- typed変数。

(サイモンが指摘したように、C99からruntime-bounded array、または「可変長配列」もあり、そのサイズは任意の変数の値で指定できます。しかし、それは別の動物です。)

static const intが実際に定数式であり、C++ 11が新しいキーワードconstexprを追加して、さらに一般的なことを可能にするC++でルールが異なることを聞いてください。 「コンパイル時に合理的に決定できる」値を持つより多くのものを包含する定数式の使用。

34
Kerrek SB

Cでは、constread-onlyの誤った呼び名です。 const変数は値を変更できます。宣言しても大丈夫です

const volatile int timer_tick_register; /* A CPU register. */

読み取りごとに異なる値を取得できますが、書き込みはできません。したがって、言語仕様はconst修飾オブジェクトnotを配列サイズに適した定数式として扱います。

31
Jens

VLAの2つの主要な代替手段:enumおよびマクロ

enumの場合:

enum N { N = 5 };
int is[N];

これは、enumメンバーが定数式であるため機能します。 enumメンバーはANSI-Cの配列のサイズにできますか?

マクロあり:

#define N 5
int is[N];

enumsの利点は、enumsにスコープがあり、コンパイル手順の一部であるため、エラーメッセージが改善される可能性があることです。

マクロの利点は、定数のタイプをより詳細に制御できることです(例:#define N 1 vs #define N 1u)、enumsはいくつかの実装定義型に固定されています: sizeof(enum sizeof(int)、always?)==しかし、これはそれほど重要ではありません場合。

VLAを避ける理由

編集:Wikipediaで、C11が可変長配列をoptional機能に委譲したことを読んでください。しかし、後半はあなたの他の質問のいくつかに答えます:)

Kerrek SBの投稿の補足として、C99(ISO/IEC 9899:1999)には可変長配列の概念があります。標準では次の例を示します。

#include <stddef.h>
size_t fsize3(int n)
{
    char b[n+3]; // variable length array
    return sizeof b; // execution time sizeof
}

sizeof演算子は次のように拡張されます。

Sizeof演算子は、そのオペランドのサイズ(バイト単位)を生成します。これは、式または括弧で囲まれた型の名前です。サイズは、オペランドのタイプから決定されます。結果は整数です。 オペランドの型が可変長配列型の場合、オペランドが評価されます;それ以外の場合、オペランドは評価されず、結果は整数定数になります。

別の素敵な例は wikipedia にあります。

静的に宣言された変数を可変長配列にすることはできません。

あなたの他の質問のいくつかに関して:

Q:コード内で定数が実際の値に置き換えられるのはいつですか?

定数がconst変数の場合、「置換」されることはなく、常にメモリの領域としてアクセスできます。これは、アドレス演算子&がまだ変数を処理する必要があるためです。ただし、変数アドレスが使用されない場合は、「置換」され、メモリが割り当てられない可能性があります。 C標準から:

実装は、ストレージの読み取り専用領域に揮発性ではないconstオブジェクトを配置できます。さらに、そのアドレスが使用されない場合、実装はそのようなオブジェクトにストレージを割り当てる必要はありません。

次の質問...

Q:変数<x>のRAMにメモリが割り当てられていないことを知っていますが、ROMの定数変数領域は値5を保持します

これはシステムによって異なります。 ROMがあり、コンパイラがROMの場所を知っている場合、それはROMに配置される可能性があります。 ROMがない場合、コンパイラ(実際にはリンカ)が持つ唯一の選択肢はRAMです。

Q:xは、コードでxが出現するすべての場所で値5に置き換えられます。しかし、これはいつ起こりますか?コンパイル時間?起動時間?前処理時間?

前述のように、これはむしろ定数の使用方法に依存します。 const変数のアドレスが使用されず、コンパイラーが十分に賢い場合、コンパイル時に。それ以外の場合、「置換」は発生せず、メモリ内の場所を含む値です。この場合、メモリ内の変数の配置はリンク時に発生します。前処理中にneverは発生しません。

4
Jimbo

言及する価値があるかもしれません、ここで使用できます

int b[] = {1, 4, 5};

要素の数が必要な場合

 size_t sz = sizeof(b)/sizeof(b[0]);

定数、フラッシュ、RAMの保存場所を決定するのはツールチェーン次第だと思います

1
Yura