C、C ++、Javaの静的変数がデフォルトでゼロで初期化される理由を知りたいですか?そして、なぜこれはローカル変数には当てはまらないのでしょうか?
7 に答える
静的変数が決定論的に初期化され、ローカル変数が初期化されないのはなぜですか?
静的変数がどのように実装されているかをご覧ください。それらのメモリはリンク時に割り当てられ、それらの初期値もリンク時に提供されます。実行時のオーバーヘッドはありません。
一方、ローカル変数のメモリは実行時に割り当てられます。スタックを増やす必要があります。あなたは前に何があったのか分かりません。必要に応じて、そのメモリをクリア(ゼロにする)できますが、実行時のオーバーヘッドが発生します。C ++の哲学は「使用しないものにはお金を払わない」ということなので、デフォルトではそのメモリをゼロにすることはありません。
OK、しかしなぜ静的変数はゼロに初期化され、他の値ではないのですか?
さて、あなたは一般的にその変数で何かをしたいです。しかし、それが初期化されているかどうかをどうやって知るのでしょうか?静的ブール変数を作成できます。ただし、それを確実に何かに初期化する必要もあります(できればfalse)。ポインタはどうですか?ランダムなゴミよりもNULLに初期化する方がよいでしょう。構造体/レコードはどうですか?内部に他のデータメンバーがいくつかあります。それらすべてをデフォルト値に初期化することは理にかなっています。ただし、簡単にするために、「0に初期化」戦略を使用する場合は、個々のメンバーを検査してそのタイプを確認する必要はありません。メモリ領域全体を0に初期化するだけです。
これは実際には技術的な要件ではありません。デフォルト値が0以外の場合、初期化のセマンティクスは正常であると見なすことができますが、それでも決定論的です。しかし、それでは、その値はどうあるべきでしょうか?0が使用される理由は簡単に説明できますが(実際には少し恣意的に聞こえますが)、-1または1024を説明するのはさらに難しいようです(特に、変数がその値を保持するのに十分な大きさではない場合など)。
また、変数はいつでも明示的に初期化できます。
また、C ++標準の8.5.6項には、「静的ストレージ期間のすべてのオブジェクトは、プログラムの起動時にゼロで初期化される」と書かれています。
詳細については、次のその他の質問を参照してください。
C ++標準のパラグラフ8.5.6は、次のように述べています。
「静的ストレージ期間のすべてのオブジェクトは、プログラムの起動時にゼロで初期化される必要があります」
(標準では、ローカル変数の初期化は未定義であるともされています)
理由については、標準には記載されていません;)1つの推測では、追加の欠点がなくても、実装はかなり簡単です。
Javaと言えば:
ローカル変数は安全性を高めるため、アクセスする前に初期化する必要があります。変数が確実に設定されているかどうかは、コンパイラーがチェックします。
静的変数またはクラス変数(オブジェクトタイプ)はnull
、で初期化されます。これは、コンパイラがコンパイル時に初期化されているかどうかを確認できないためです。初期化されていない変数にアクセスした場合にプログラムを失敗させる代わりに、を使用して暗黙的に初期化されnull
ます。
ネイティブ型の変数は値を取得できないため、null
非ローカル変数はフォールバックとして0
またはで初期化されます。false
それは確かに最善の解決策ではありませんが、私はより良い解決策を知りません。;-)
したがって、ある程度、これらは言語設計者の設計上の決定にすぎません。しかし、Javaでこれらの決定を行う理由としては、次のようなものが考えられます。
- 静的/メンバー変数の場合、それらを何かに初期化する場合は、ゼロが便利な値です。これは、(a)「特別なものに設定されていない」ことを意味するのに一般的に適切な値であり、とにかく、カウンターなどの場合に選択されます。(b)内部的には、「特別な」値にゼロを使用できる可能性があります。特に、オブジェクト参照の場合はnullを表します。
- ローカル変数の場合、デフォルトを指定しないと、変数が読み取られる前にプログラマーに値を設定させるルールが許可されます。これは、コンパイラーが特定のエラーを検出できるようにするのに実際に役立ちます。
ローカル変数の場合、ローカル変数を宣言することも考えられますが(バイトコード/マシンコードレベルでは、基本的にスタックスペースの割り当て/スタックポインタの移動を意味します)、特定のコードパスで実際に書き込み/読み取りを行うことはありません。したがって、デフォルトがない場合は、そのような場合にデフォルトを設定するという不必要な作業を回避できます。
繰り返しますが、これらはある程度設計上の決定です。これらは基本的に、JVM実装に便利である可能性が高いものとプログラマーに便利である可能性が高いものとの間のトレードオフです。
注意:C / C ++では、「静的」変数はJavaの静的変数とは異なる意味を持ちます。
これは、C /C++の「使用した分だけ支払う」という概念と関係があります。
静的変数の場合、コードを生成せずに初期化を行うことができます。オブジェクトファイルには、データセグメント内の変数の初期値が含まれており、OSが実行可能ファイルをロードすると、プログラムの実行を開始する前に、このデータセグメントをロードしてマップします。
ローカル変数の場合、一度は初期化されないため、コードなしで初期化する方法はありません。スコープに入るたびに初期化する必要があります。また、それらはスタックに割り当てられ、割り当てが発生すると、一般的な場合のスタックの初期値は、単に以前にあったものになります(スタックを以前よりも大きくするというまれな瞬間を除く)。
したがって、ローカル変数を暗黙的に初期化するには、プログラマーが明示的に命令することなくコードを生成する必要があります。これは、その「哲学」に反します。
Javaについては、私が知る限り、変数は静的であるかどうかに関係なく、プログラムがスコープに入るときに常に初期化されます。それらの間の唯一の重要な違いは、静的変数のスコープがプログラム全体であるということです。それを考えると、動作はそれらすべての間で一貫しています。
これは単なる推測ですが、実装が簡単で便利なため、静力学の場合と同じようになる可能性があります。
コンパイラーは、すべての変数を1つの連続したメモリー領域に同時に割り当ててから、呼び出されるmemset()
前にコードを発行して(1回の呼び出しで)それをクリアすることができmain()
ます。多くの場合、オペレーティングシステムの実行可能ファイル形式が「 bssセクション」をサポートしている場合は、その形式の機能に依存することもあります。この形式は、代わりにローダーによってクリアされます。これにより、実行可能ファイルのスペースが節約されます。
static unsigned char megabyte[1 << 20];
実行可能ファイルはメガバイト単位で拡張されません。
ローカル変数の場合、これらはいずれも適用されません。それらは「オンザフライ」(通常はスタック上)に割り当てられ、いずれにせよすぐに割り当てられるため、それらをクリアするのはリソースの無駄になります。
私はJavaについて何も知りませんし、Javaのstatics/localsとは違うのではないかと思います。
cとc++に関しては、プログラマーがコードの効果を気にし、制御することを愛することについてです。ローカル変数を初期化すると、プログラムがスコープに入るたびに追加のコードが実行されることになります。災害の可能性がある頻繁に呼び出される関数の場合。