なぜint a [5] = {0}とint a [5] = {1}(欠落している機能)[閉じた]の違い
-
23-10-2019 - |
質問
このような配列を初期化するとき int a[5] = {0}
, 、コンパイラは5つの要素すべてを作成します。これは、非常に優れたコンパクトなイニテリアル化と有用な機能です。
しかし、なぜコンパイラが初期化しないのだろうか int a[5]={1}
同様に? 5つの要素すべてを作成しないのはなぜですか?なぜ標準はそれを義務付けないのですか?それは素晴らしい機能ではないでしょうか?見逃していませんか?
また、初期化器の要素の数が配列のサイズよりも少ない場合、コンパイルは初期イザーの最後の要素で残りの要素を初期化できます。意味、 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}
この例では、[0]は2に初期化されます。1 4に初期化され、[2]からa [7]が0に初期化され、[9]が10に初期化されます。
非常に興味深い。しかし、この機能でさえC ++ではありません。
解決
個人的には、アレイだけで最後のルールを繰り返す別のルールの代わりに、固定デフォルトの初期イザーがより多くの「論理」(つまり単純な」を持っていることがわかります。それは「実用的」に見えるかもしれません(つまり、有用)が、IMOはより論理的に複雑です。
しかし、私はあなたが作っていると思います 大きい C ++のような言語にロジックを適用しようとすることを間違えます。
C ++は、ルールが長い進化の歴史の結果である複雑な言語であり、その現在の形式は多くの人々の仕事の結果であり、正式な委員会(最後の部分だけでも説明できます) なんでも).
C ++のような言語は論理によって推測されることはできません。歴史のように研究する必要があります。あなたがいない限り ハリ・セルドン 論理的推論を使用して歴史を推測する方法は本当にありません。
勉強する代わりにロジックを使用しようとすると、C ++の場所がたくさんあります。いくつか名前を付けるために...
- Default Dispatch static(すなわち、なぜデフォルトのディスパッチが静的ですか 違う)?
- ヌルポインターのキーワードがないのはなぜですか?
- なぜ署名されていない2つの違いが署名されていないのですか?
- 署名されたものと署名なしの合計が署名されていないのはなぜですか?
- 署名されていない場合、「要素 Z_ {2^n}「では、なぜサイズが署名されていないのですか?
- どうして
std::string s; s=3.141592654;
完全に有効なC ++ですか? - なぜC ++ 0x
i = i++ + 1;
未定義の動作ですi = ++i + 1;
有効ですか? - どうして
double x=3.14; int y(int(x));
意味がありませんy
3になりますか?
他のヒント
5つの要素すべてを作成しないのはなぜですか?
あなたが何を誤解しているからです {}
意味。 (実際、C ++ではこれを行うためのより良い方法は {}
それよりも {0}
)。構文 {0}
集約内のすべての要素がゼロに設定されることを意味するわけではありません。むしろ、指定された変数に割り当てられた最初の要素ゼロ(C ++のクラスタイプのいずれかにすることができる)を持つ集計が必要であると言います。骨材は通常、1つの値ゼロよりも多くのフィールドを持っているため、集約の残りの要素は デフォルトが作成されました. 。ビルトインまたはポッドタイプのデフォルト値は、すべてのフィールドをゼロに設定することです。そのため、集約全体をゼロに効果的に設定します。
なぜ具体的には、以下を検討してください。現在の基準によると、以下のアサーションはどれも失敗しません。
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
提案されている標準的な変更に参加することは?上記のように、それを集約初期イザーの最後の要素として定義したとしても、残りの要素がデフォルトで構築されていると仮定する既存のコードとの互換性を破るでしょう。
編集: あなたの質問は配列の観点から尋ねられることに気付いただけですが、答えは構造または配列のいずれかで同じであるため、それは本当に問題ではありません。
編集2: これを標準に合わせてより多くにするために、クラス/構造への参照は、構造と配列のケースをカバーする「集計」に置き換えられています。
はい、彼ら できる それをしましたが、彼らはそうしませんでした、そして、そのような行動を変えるには今は遅すぎます。 CとC ++の背後にある決定は、ほぼすべてのステップでパフォーマンスとミニマリズムに与えられた考えを持って行われたので、他に何もなければ、ここにも登場すると思います。
このような機能は、私をそれほど素晴らしいものとして打つことはありません。これは非常にシンプルな構文砂糖であり、とにかく0以外のものにそのような配列を初期化する必要性を見つけることはめったにありません。
一般的なランタイムライブラリは、データを0に簡単に初期化できる機能を提供します。一般的に、これは特定に保存されます セクション コンパイラとリンカーによって編成された実行可能ファイル。プログラムスタートアップでは、ランタイムスタートアップコードは次のようなものを使用します memset()
初期化されたすべてのデータを0にクリアするには、ゼロバイトを実行可能ファイル自体に保存する必要がないことを意味します。
逆は、あなたが何かにデータを初期化する場合です 以外 ゼロ、そのデータのバイトは、自動初期化装置の初期化のみがゼロのみであるため、実行可能ファイル自体に保存する必要があります。
したがって、あなたがの大きな配列を宣言するなら char
(メガバイトと言う?)そして、それを初期化して、たとえば、 {0}
, 、次に、その配列の実行可能ファイルにバイトが保存されません。一方、あなたがそれを初期化する場合は {1}
あなたのスキームの下で、のメガバイト 1
バイトは、実行可能ファイル自体に保存する必要があります。イニシャルリストの1人の文字を変更することにより、実行可能ファイルのサイズはメガバイトによって増加します。
私はそのようなスキームが違反すると信じています 最も驚きの原則.