2017/02/09

[c/c++]0を代入したグローバル変数は初期値ありと見なされないようだ

以前から気になっていた、Cのグローバル変数を0で初期化したときの扱いだ。

01: #include 
02: 
03: static int a;
04: 
05: int main(int argc, char *argv[])
06: {
07:     printf("a=%d\n", a);
08: }

こう書いても、

01: #include 
02: 
03: static int a = 0;
04: 
05: int main(int argc, char *argv[])
06: {
07:     printf("a=%d\n", a);
08: }

こう書いても、普通のCコンパイラならaはmainが始まる前に0で初期化されるようにビルドする。
「普通は」と書いたのは、組み込み環境で、ものすごくメモリが少なかったら、そういうmainが始まる前の処理を呼ばないようにしてしまうという選択ができなくもないからだ。
コンパイラがやると言うよりも、実装した人がいじくることが多いだろうから、そういう環境は省略だ。

 

ただ、今回の話は、そのmainが始まる前の部分になる。
プログラムを実行すると、C言語ではmainから始まると表現しているが、グローバル変数なんかはmainに呼ばれる前に初期化されるので、当然mainより前が存在する。

その、main前の処理(C Runtimeとか呼ぶのか?)の中であれこれやっていたのだが、グローバル変数の初期化も行われていた。
やるのは、

  • 初期値無しグローバル変数を0でクリア
  • 初期値あり変数に初期値代入

だ。
その実装がどうなっていたか覚えていないが、memset()みたいなアセンブラ処理と、memcpy()みたいなアセンブラ処理があったような気がする(まだC89やANSI Cより前のコンパイラだった。。。)。


さて、昔話はここまでにして、気になっているのが何かを説明する。

上に2つソースを載せたが、どちらもaは0になる。
なるのだが、上は初期値無しなのだが、下は初期値あり(0)とみなすのか、0になるのがわかっているから初期値無し扱いなのか、だ。
初期値あり扱いになると、初期値をROMに持っておいて、それを初期値あり領域にコピーするような作りになってしまうので、できれば0初期値の場合は初期値を持っていないという扱いにしてくれるとうれしい。

じゃあ、最初から初期値を書かなければよいではないか、となるのだけど、初期値がある変数をずらずら書いているのに、0のところだけ何も書かないと、すっきりしないのだ。
そう、気分の問題である。
ソースファイルって、リズミカルに書くようにして、リズムが悪いところにバグがあるかも、というチェックができたらうれしいじゃないか。

 

■初期値無し
$ gcc -o tst -O0 -Xlinker -Map=map.map test.c
$ size tst
   text    data     bss     dec     hex filename
   1226     560       8    1794     702 tst

■初期値0
$ gcc -o tst -O0 -Xlinker -Map=map.map test.c
$ size tst
   text    data     bss     dec     hex filename
   1226     560       8    1794     702 tst

よかった、同じようだ。

試しに、初期値として3を代入させた。

text    data     bss     dec     hex filename
1226     564       4    1794     702 tst

sizeコマンドでは以下が出力される。

    • text : コード
    • data : 初期値あり変数
    • bss : 初期値無し変数

初期値があると、ちゃんと初期値あり変数の方に4byteが移動している。
じゃあ、初期値自体はどこにあるのかというと、textのはずだ。
textに変化が見られないのは、alignmentの波に取り込まれてしまったからだろうか。。。

 

ただ、これはグローバル変数にべたっと書いた場合で、構造体の場合はまた事情が違うだろう。
構造体の中のメモリが、初期値ありとなしで別になっていたら困るからね。。。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。