4

このように配列を初期化するint a[5] = {0}と、コンパイラは 5 つの要素をすべて 0 にします。これは非常に優れたコンパクトな初期化であり、便利な機能です。

しかし、なぜコンパイラがint a[5]={1}同様に初期化しないのだろうか? 5つの要素すべてを1にしないのはなぜですか? 規格がそれを義務付けていないのはなぜですか? それは素晴らしい機能ではないでしょうか?欠けていませんか?

また、初期化子の要素数が配列のサイズよりも少ない場合、コンパイルは初期化子の最後の要素で残りの要素を初期化できます。つまり、int a[5]={1,2,3}と同等int a[5]={1,2,3,3,3}です。同様に、int a[10]={1,2,3,0}は と同等int a[10]={1,2,3,0,0,0,0,0,0,0};です。

標準で義務付けられている場合、それはすべて素晴らしい機能ではないでしょうか? または、この欠落している機能には正当な理由がありますか?


また、C99 には指定イニシャライザと呼ばれるものがあり、次のように使用されます。

次の例のように、指定された初期化子を通常の初期化子と組み合わせることができます。

int a[10] = {2, 4, [8]=9, 10}

この例では、a[0] は 2 に初期化され、a 1は 4 に初期化され、a[2] から a[7] は 0 に初期化され、a[9] は 10 に初期化されます。

非常に興味深い。しかし、この機能でさえ C++ にはありません。

4

4 に答える 4

10

5つの要素すべてを1にしないのはなぜですか?

{}意味を誤解しているからです。(実際、C++ では、これを行うより良い方法は{}ではなく{0})。この構文{0}は、集約内のすべての要素をゼロに設定することを意味するものではありません。むしろ、指定された変数 (配列または C++ のクラス型のいずれか) に最初の要素ゼロが割り当てられた集約が必要であることを示しています。集約には通常、その 1 つの値ゼロよりも多くのフィールドがあるため、集約内の残りの要素はデフォルトで構築されます。ビルトインまたは POD タイプのデフォルト値は、すべてのフィールドをゼロに設定するため、実質的に集計全体をゼロに設定したことになります。

具体的には、以下の理由を考えてみてください。現在の標準によれば、以下のアサーションはどれも失敗しません。

struct abc
{
    char field1;
    int field2;
    char field3;
};

int main()
{
    abc example = {'a', static_cast<int>('b')};
    //All three asserts pass
    assert(example.field1 == 'a');
    assert(example.field2 == static_cast<int>('b'));
    assert(example.field3 == '\0');

    int example2[3] = {static_cast<int>('a'), 42};
    assert(example2[0] == static_cast<int>('a'));
    assert(example2[1] == 42);
    assert(example2[2] == 0);
}

field3提案された基準変更において、 の価値はどのようなものになると予想しますか? 上に示したように、集約初期化子の最後の要素として定義したとしても、残りの要素がデフォルトで構築されていると想定する既存のコードとの互換性が失われます。


編集:あなたの質問は配列に関して尋ねられていることに気付きましたが、答えは構造体でも配列でも同じであるため、実際には問題ではありません。

EDIT2:これをより標準に合わせるために、クラス/構造への参照は、構造と配列のケースをカバーする以下の「集約」に置き換えられました。

于 2011-01-23T07:11:05.970 に答える
4

はい、彼らはそれを行うことができたかもしれませんが、そうしませんでした.そのような行動を変えるには今では遅すぎます. C と C++ の背後にある決定は、ほぼすべてのステップでパフォーマンスとミニマリズムを考慮して行われたため、他に何もないとしても、ここでも影響が及ぶと思います。

そのような機能は、それほど素晴らしいものではありません。これは非常に単純な構文糖衣であり、そのような配列を 0 以外に初期化する必要はめったにありません。

于 2011-01-23T07:05:37.563 に答える
2

一般的なランタイム ライブラリには、データを 0 に簡単に初期化できる機能が用意されています。一般的に、これは実行可能ファイルの特定のセクションに格納され、コンパイラとリンカーによって編成されます。プログラムの起動時に、ランタイム起動コードはmemset()、初期化されたすべてのデータを 0 にクリアするようなものを使用します。これは、ゼロ バイトを実行可能ファイル自体に格納する必要がないことを意味します。

逆に言えば、データをゼロ以外に初期化する場合、自動初期化子はゼロに初期化するだけなので、そのデータのバイトは実行可能ファイル自体に格納する必要があります。

したがって、大きな配列char(メガバイトと言いますか?) を宣言し、それを で初期化すると、{0}その配列の実行可能ファイルにはバイトが格納されません。一方、{1}スキームの下でそれを初期化する場合、メガバイトの1バイトを実行可能ファイル自体に格納する必要があります。イニシャライザ リストの 1 文字を変更すると、実行可能ファイルのサイズが 1 メガバイト増加します。

私は、そのような計画は最小の驚きの原則に違反すると信じています。

于 2011-01-23T07:12:38.067 に答える
0

個人的には、配列に対して最後のイニシャライザを繰り返すという別のルールの代わりに、固定のデフォルト イニシャライザを使用する方がより「論理的」(つまり単純) だと思います。それは「実用的」(つまり有用)に見えるかもしれませんが、IMO は論理的にもっと複雑です。

とはいえ、C++ のような言語にロジックを適用しようとするのは大きな間違いだと思います。

C++ は複雑な言語であり、その規則は長い進化の歴史の結果であり、その現在の形は、多くの人々や正式な委員会の作業の結果です (最後の部分だけで何でも説明できます)。

C++ のような言語は、論理によって推論することはできず、歴史のように研究する必要があります。あなたがハリ・セルドンでない限り、論理的な推論を使用して歴史を推測する方法は本当にありません.

C++ には、勉強せずにロジックを使おうとすると、かなり苦労するところがあります。ほんの数例を挙げると…

  • デフォルトのディスパッチはなぜ static なのですか (つまり、間違っています)?
  • ヌル ポインターのキーワードがないのはなぜですか?
  • 2 つの unsigned の違いが unsigned であるのはなぜですか?
  • 署名されたものと署名されていないものの合計が署名されていないのはなぜですか?
  • unsigned が「 Z_{2^n}の要素」を意味する場合、なぜサイズが unsigned なのですか?
  • std::string s; s=3.141592654;C++ が完全に有効なのはなぜですか?
  • なぜ C++0Xi = i++ + 1;は未定義の動作でi = ++i + 1;有効なのですか?
  • will が 3 になるdouble x=3.14; int y(int(x));という意味ではないのはなぜですか?y
于 2011-01-23T08:27:09.030 に答える