web-dev-qa-db-ja.com

nがconst値である場合、int x [n]が間違っているのはなぜですか?

これを行うことがなぜ間違っているのか理解できません:

const int n = 5; 
int x[n] = { 1,1,3,4,5 };

nはすでにconst値ですが。

これを行うことは、GNUコンパイラーにとって正しいようです:

const int n = 5;
int x[n]; /*without initialization*/

私はC99のVLA機能を知っており、それは何が起こっているかに関連していると思いますが、バックグラウンドで何が起こっているのかを明確にする必要があります。

38
Yahia Farghaly

覚えておくべき重要なことは、constと "constant"は2つのまったく異なることを意味するということです。

constキーワードは、実際には「読み取り専用」を意味します。 定数は、_42_または_1.5_(または列挙型または文字定数)などの数値リテラルです。 定数式は、_2 + 2_など、コンパイル時に評価できる特定の種類の式です。

したがって、宣言が与えられます:

_const int n = 5;
_

nobjectの値を参照し、定数式としては扱われません。一般的なコンパイラは、nへの参照を最適化し、リテラル_5_に使用するのと同じコードに置き換えますが、これは必須ではありません。また、式がconstantは、現在のコンパイラの巧妙さではなく、言語によって決定されます。

const(読み取り専用)とconstant(コンパイル時に評価)の違いの例は次のとおりです。

_const size_t now = time(NULL);
_

constキーワードは、初期化後にnowの値を変更することは許可されていないことを意味しますが、time(NULL)の値は実行時まで明らかに計算できません。

したがって、この:

_const int n = 5;
int x[n];
_

cでは、constキーワードがない場合よりも有効ではありません。

言語could(そしてIMHOはおそらくそうすべきです)はnを定数式として評価します。それはそのように定義されていないだけです。 (C++にはそのようなルールがあります。厄介な詳細については、C++標準または適切なリファレンスを参照してください。)

値が_5_の名前付き定数が必要な場合、最も一般的な方法はマクロを定義することです。

_#define N 5
int x[N];
_

別のアプローチは、列挙定数を定義することです。

_enum { n = 5 };
int x[n];
_

列挙型定数は定数式であり、常にint型です(つまり、このメソッドはint以外の型では機能しません)。そして、それは間違いなくenumメカニズムの乱用です。

1999標準以降、配列は一定でないサイズで定義できます。これはVLA、または可変長配列です。このような配列はブロックスコープでのみ許可され、初期化子がない場合があります(コンパイラーは、初期化子に正しい数の要素があることを確認できないため)。

しかし、元のコードを考えると:

_const int n = 5; 
int x[n] = { 1,1,3,4,5 };
_

コンパイラに初期化子から長さを推測させることができます。

_int x[] = { 1,1,3,4,5 };
_

そして、配列のサイズから長さを計算できます。

_const int x_len = sizeof x / sizeof x[0];
_
36
Keith Thompson

nconst値であるのに、なぜint x[n]が間違っているのですか?

nは定数ではありません。 constは、nが「読み取り専用」変数であり、プログラムの実行中に変更してはならないことを約束するだけです。
c では、 c ++ とは異なり、const修飾変数は定数ではないことに注意してください。したがって、宣言された配列は可変長配列です。
イニシャライザリストを使用して可変長配列を初期化することはできません。

C11-§6.7.9/ 3:

初期化されるエンティティのタイプは、サイズが不明な配列、または可変長配列タイプではないである完全なオブジェクトタイプでなければなりません。

#defineまたはenumを使用して、nを定数にすることができます

#define n 5
int x[n] = { 1,1,3,4,5 };   
21
haccks

配列を徹底的に初期化する場合は、コンパイラに配列サイズを推測させる方が簡単で、安全で、保守しやすくなります。

int x[] = { 1,1,3,4,5 };
const int n = sizeof(x) / sizeof(*x) ; 

次に、配列サイズを変更するには、サイズを変更するのではなく、初期化子の数を変更するだけですおよび一致する初期化子リスト。イニシャライザーが多い場合に特に便利です。

7
Clifford

nconstですが、VLAを作成する場合を除いて、これを使用して配列のサイズを定義することはできません。ただし、初期化子リストを使用してVLAを初期化することはできません。

マクロを使用して、固定サイズの配列を作成します。

#define ARRAY_SIZE 5

int x[ARRAY_SIZE] = { 1,1,3,4,5 };
3
R Sahu

あなたのコードはここでmyfunc()と意味的に異なりますか?

void myfunc(const int n) { 
    int x[n] = { 1,1,3,4,5 };
    printf("%d\n", x[n-1]);
    *( (int *) &n) = 17;    //  Somewhat less "constant" than hoped...
    return ;
}

int main(){
    myfunc(4);
    myfunc(5);
    myfunc(6);  //  Haven't actually tested this.  Boom?  Maybe just printf(noise)?
    return 0;
}

nは本当にすべて一定ですか?コンパイラーがx[]に割り当てる必要があるスペースはどれくらいだと思いますか(そうするのはコンパイラーの仕事なので)?

他の人が指摘しているように、cv-qualifier constnotは、「コンパイル中およびコンパイル後は常に一定の値」を意味します。これは、「ローカルコードが変更されることはないはずの値」を意味します。

2
Eric Towers