2015年7月18日土曜日

C言語における局所変数とグローバル変数の確保過程

きっかけ

C言語にて構造体を新規作成し返却する関数を書いてた際、malloc関数で領域を確保する必要があると聞いた。もし確保しなければ、戻り先でこの関数が作成した構造体が使えないとのこと。
例:
struct str1 func1(void)
{
  struct str1 target;
  memset(&target, 0 sizeof(str1));
  target.member = 1;
  return target;
}

int main(void)
{
  struct str1 retval = func1();
  // str1 は使えない
}
参照さえあれば変数が生き延びるJavaと大分勝手が違う。また「〜と聞いた」と濁してるのは、私がC言語の言語仕様の当たり方に慣れていないため。C99 にあたる JISX3010 は探し当てることができたが、いずれ「どの言語仕様」が理由で「malloc関数で領域を確保する必要がある」と言い切りたい。

結論

関数内変数(自動変数と呼ばれる1)はその関数が呼び出された際にアプリケーションのメモリ空間中スタック内に確保され、関数を抜ける時にleave命令が下されるとスタックから削除される。mallocが行われた場合どうなるかは宿題。
1 参考: 橋本他 p.189

アセンブラにおける関数呼び出し

下記の様なC言語のコードを考える。

int main(void)
{
  func(2, 3);
}

funcが実行される時、スタック領域に対し次の手順が行われる。

  1. 右(最後尾)から順に引数をスタック領域に積み上げる(push OPコード)
  2. funcの実行を終えた後の戻り先コード領域のアドレスをさらにスタック領域に積み上げ、%eip を func の命令コードに移動させる(call OPコード)6
  3. func内容の実行に先立ち、現在の %esp の内容が %ebp にコピーされる8
  4. funcの中で自動変数(static変数は含まれない)の確保が行われている場合、スタック領域の上に追加されていく。この時自動変数は%ebp+nの形で表現される。8
  5. ret OPコードの前段階としてleave OPコードが実行される。これに伴い %ebp が元に戻り自動変数が解放される9
  6. func内の処理がreturnに至った時、スタック領域に保存された戻り先に %eip を戻す(ret OPコード)6
  7. 引数の数(上記コードの場合2と3の2つ)だけpopを行いスタック領域の帳尻合わせを行う。このコードはアセンブラで引数の数に基づき手動で調整される必要がある7
スタック領域
OSが実行中のプログラムに割り当てるメモリ空間の内訳にはコード領域、データ領域、スタック領域が存在する2
レジスタ
CPUについてる演算用メモリ1
%esp
Stack Pointer3. x86のレジスタ名称の一つ4。先頭の%はGAS(The GNU Assembler)において後に続く表現がレジスタ名であることを示す5
%ebp
Base Pointer3. x86のレジスタ名称の一つ4。先頭の%は%espと同じ。
%eip
x86のレジスタ名称の一つ4。Instruction Pointer. 「どこの命令をいま現在実行しているのかを指し示すレジスタである」。PC(Program Counter)と呼ばれることもある3。先頭の%は%espと同じ。

1 参考: 橋本他 p.48-49 / 2 参考: 橋本他 p.110 / 3 参考: 橋本他 p.27 / 4 参考: 橋本他 p.42 / 5 参考: 橋本他 p.33 / 6 参考: 橋本他 p.113 / 7 参考: 橋本他 p.117 / 8 参考: 橋本他 p.193 / 9 参考: 橋本他 p.194

参考書籍

  • 橋本洋志, 小林裕之, 冨永和人(平22)『組込みユーザのためのアセンブリ/C言語読本』オーム社

0 件のコメント:

コメントを投稿