2019年1月28日月曜日

.bssセクションのグローバル変数の初期化について

こんにちは、ふじかーです。

先日現場で グローバル変数の初期化 が話題に上がりました。

「このグローバル変数で判定してますね。値、何が入ってるんだろ」
「定義時には特に初期値入れてないですね」
「あら、判定までずっと値入れてないですね。じゃ値は0ですか」
「なんか不定値になることありませんでしたっけ?」
「必ず0初期化されるんじゃなかったですかね?」

昔調べた気がするけど、正確にはどうだったか・・
と曖昧になっていたので、少し確認しなおしました。

セクション


プログラムはメモリに配置されて実行されるわけですが、メモリ上には
プログラム内の「機械語」や「変数」「定数」など、各種用途ごとに分かれて配置されています。

その単位のことをセクションといいますが
今回のグローバル変数などは『.bss セクション』という領域に格納されます。

.bssセクションに格納される変数の種類は

・初期値 なし の グローバル変数
・初期値が 0 の グローバル変数
・初期値 なし の 静的(=static)なローカル変数
・初期値が 0 の 静的(=static)なローカル変数

となっています。

この.bss領域は「全て0初期化されているべし」とC言語の規約で規定されているため
プログラム実行前に、領域を確保して丸ごと0初期化されるようです。

wikipedia
通常、bssセクションに割り当てられたメモリは
プログラムローダーがプログラムをロードするときに初期化する。
main() が実行されるより前に
Cランタイムシステムがbssセクションにマップされたメモリ領域をゼロで初期化する。

ちなみに、初期値あり(0以外) のグローバル変数や静的ローカル変数は?というと
『.dataセクション』 という領域に格納されます。
プログラム実行前に、領域を確保して値を格納していきます。

また、「const」がつけられる定数が格納されるのは
『.rodataセクション』 という領域です。

さらに関数などプログラムの機械語そのものは
『.textセクション』 という領域です。


メジャーなのはそのあたりですが、
ビルド後の実行ファイルの各セクションがどのように配置されるか、
下記のコマンドで確認することができます。

>objdump -h a.exe



落とし穴


初めの話に戻り、.bssは 言語の仕様として0初期化される と決まっているので

「じゃあグローバル変数は自動的に『起動時0初期化処理』されてるものとして
 使うときも特に初期化せずに使えば良いね」

と考えられるかというと、そうでも無いようです。


場合によって、例えば組み込み機器などで

「電源投入からプログラム起動までの時間を極力短くしたい。
 メモリの初期化時間ですら、可能なかぎり限界まで削りたい」

というシーンでは、

やろうと思えばこの『起動時0初期化処理』を省くことができるようで、
そうすると実行時のグローバル変数には「不定値」が入っていたりするとの事。

やはり変数を使う前には、明示的に値を入れて初期化してから使う方がよさそうです。
見つけにくいバグや、不要な不安やトラブルを避けられますもんね。
今後実装する時やレビューする際は、気を付けたいと思います。

※関連記事:メモリのセクション内の配置を確認してみた

2 件のコメント:

  1. 今、C言語やハードウェアの勉強をしているのでとても分かりやすかったです!

    返信削除
    返信
    1. コメントありがとうございます!

      参考になったようで良かったです。
      関連記事で続きの話を書いてるので、良かったらそちらも覗いてみてください。

      メモリのセクション内の配置を確認してみた
      http://aimek-developer.blogspot.com/2019/02/blog-post.html

      削除