C99に準拠した内容です。
C11でも特に変わっていなさそう。
C言語の仕様で次の構造体の初期化はどうなるのか、少しまとめておきたい。
# include<stdio.h>
struct S
{
int val;
void *ptr;
int val2;
};
int main(int argc, char *argv[])
{
struct S a_var = {0};
static struct S s_var;
// 自動変数の表示
printf("%d\n%d\n", a_var.val, a_var.val2);
if (a_var.ptr == NULL)
{
// NULLのときの処理
}
// 静的変数の表示 => 初期化していないので、.bssに置かれる。
printf("%d\n%d\n", s_var.val, s_var.val2);
if (s_var.ptr == NULL)
{
// NULLのときの処理
}
return 0;
}
下記の2ページの内容をまとめてみる。
スタックオーバーフロー:C言語のポインタ変数を含む構造体初期化について
スタックオーバーフロー:how about .bss section not zero initialized
算術型:int / long / char 等
初期化状態 | auto / 算術型 | static / 算術型 | グローバル / 算術型 |
---|---|---|---|
未 | 不定 | 0 | 0 |
済 | 初期化値 | 初期化値 | 初期化値 |
初期化状態 | auto / ポインタ型 | static / ポインタ型 | グローバル / ポインタ型 |
---|---|---|---|
未 | 不定 | NULL | NULL |
済 | 初期化値 | 初期化値 | 初期化値 |
配列や、構造体の先頭要素のみを明示的に初期化した場合
(上記ソースの「struct S v={0};」みたいなやり方)は
先頭要素以外staticやグローバル変数の未初期化変数と同様の方法で初期化される。
ということで、上記の構造体を利用するコードでは、初期化が完了すると以下の値を持つことになる。
a_var.val -> 0
a_var.ptr -> NULL
a_var.val2 -> 0
s_var.val -> 0
s_var.ptr -> NULL
s_var.val2 -> 0
ちゃんとC99の規格に準拠していれば、NULLは「数値型に変換した時0と一致する」ということなのでC言語で言うところのint型の0とNULLの値は一致しなければならない。
(ただし、C言語のソース上ではその比較はやってはいけない(可読性下がるからマジやめてね)って話と言う認識でいる)
ということは、自作でOS作ってる人はプログラムローダとか作るときは責任持って.bssをゼロ埋めしてあげる必要があるのだなぁ。
間違っても1とかで埋めてはいけない。
[追記]
スタックに格納した変数はコンパイラが0クリアするコードを書いてくれるはずだよ と言うツッコミが有ったので、BinaryNinjaでディスアセンブルしてみる。
スタックに確保されたやつは赤枠の部分みたいに、代入コードが生成されて初期化されるのを忘れていた。
指摘されて あー そういえば!ってなった。
ちなみに、.bssの方はどうなっているかをobjdumpで見る。
とりあえずBinaryNinjaでも見てみる。
.bssの初期化値はファイルに書かれて無いはずだけど
(.bssはセクションフラグにLOADが存在しない、ALLOCのみ)
BinaryNinjaが勝手に解釈して勝手にゼロを代入している。
だからまぁ、ローダー側はC言語の規格に準拠する処理系と言いたいなら、.bssをゼロ埋めしてあげよう。
以上