C vs Assembler

C program

了解です。では、先ほどの Cコード例 (addmain) を使って、スタック上の配置とアセンブラ命令の対応 を一緒に整理します。
これで「Cの変数・引数がどうスタックに置かれているか」が一目でわかります。


1. Cコード

int add(int a, int b) {
int c = a + b;
return c;
}

int main() {
int result = add(3, 4);
return result;
}


2. スタックのイメージ

add関数呼び出し直後

高アドレス
-------------------

戻りアドレス ← call add で自動的に push
-------------------

引数 b [ebp+12] ← スタックに積まれた順番
-------------------

引数 a [ebp+8] -------------------
旧EBP [ebp] ← push ebp
-------------------

ローカル変数 c [ebp-4] -------------------
低アドレス

main関数のスタック(add呼び出し前)

高アドレス
-------------------

戻りアドレス ← call main で push
-------------------

旧EBP [ebp] ← push ebp
-------------------

ローカル変数 result [ebp-4] -------------------
低アドレス

3. アセンブラ命令とスタック対応

add関数

命令 役割 スタック上の意味
push ebp 前のEBPを退避 スタックに旧EBPを積む
mov ebp, esp フレームポインタ設定 EBPを新しいフレームの基準に
mov eax, [ebp+8] aをeaxにロード 引数aを読み込む
add eax, [ebp+12] bをeaxに加算 引数bを読み込んで加算
pop ebp フレームポインタ復元 スタックを元に戻す
ret 呼び出し元に戻る スタック上の戻りアドレスにジャンプ

main関数

命令 役割 スタック上の意味
push ebp 前のEBPを退避 スタックに旧EBPを積む
mov ebp, esp フレームポインタ設定 EBPを新しいフレームの基準に
sub esp, 4 ローカル変数確保 result用に4バイト確保([ebp-4])
push 4 addの第2引数 スタックに積む
push 3 addの第1引数 スタックに積む
call add add関数呼び出し リターンアドレスをpushしてジャンプ
add esp, 8 引数削除 pushした引数2つ分をスタックから削除
mov [ebp-4], eax resultに保存 addの戻り値をローカル変数resultに書き込む
mov eax, [ebp-4] return result 戻り値としてeaxにロード
pop ebp フレームポインタ復元 スタックを元に戻す
ret 呼び出し元に戻る スタック上の戻りアドレスにジャンプ

4. ポイントまとめ

  1. 引数 → スタック上で [ebp+8], [ebp+12], ...

  2. ローカル変数[ebp-4], [ebp-8], ...

  3. 戻り値eax に格納

  4. 関数呼び出し時のスタックの流れ

    • 引数を積む → callで戻りアドレスが積まれる → calleeがフレームを作る → 処理 → retで戻る

  5. フレームポインタ(EBP) はローカル変数・引数を固定オフセットで参照するための目印


💡 補足:

  • スタックは LIFO(後入れ先出し)

  • push で上に積む、pop で取り出す

  • esp は常に「スタックの一番上」、ebp は「フレームの基準」

C program

了解です。では、先ほどの Cコード例 (addmain) を使って、スタック上の配置とアセンブラ命令の対応 を一緒に整理します。
これで「Cの変数・引数がどうスタックに置かれているか」が一目でわかります。


1. Cコード

int add(int a, int b) {
int c = a + b;
return c;
}

int main() {
int result = add(3, 4);
return result;
}


2. スタックのイメージ

add関数呼び出し直後

高アドレス
-------------------

戻りアドレス ← call add で自動的に push
-------------------

引数 b [ebp+12] ← スタックに積まれた順番
-------------------

引数 a [ebp+8] -------------------
旧EBP [ebp] ← push ebp
-------------------

ローカル変数 c [ebp-4] -------------------
低アドレス

main関数のスタック(add呼び出し前)

高アドレス
-------------------

戻りアドレス ← call main で push
-------------------

旧EBP [ebp] ← push ebp
-------------------

ローカル変数 result [ebp-4] -------------------
低アドレス

3. アセンブラ命令とスタック対応

add関数

命令 役割 スタック上の意味
push ebp 前のEBPを退避 スタックに旧EBPを積む
mov ebp, esp フレームポインタ設定 EBPを新しいフレームの基準に
mov eax, [ebp+8] aをeaxにロード 引数aを読み込む
add eax, [ebp+12] bをeaxに加算 引数bを読み込んで加算
pop ebp フレームポインタ復元 スタックを元に戻す
ret 呼び出し元に戻る スタック上の戻りアドレスにジャンプ

main関数

命令 役割 スタック上の意味
push ebp 前のEBPを退避 スタックに旧EBPを積む
mov ebp, esp フレームポインタ設定 EBPを新しいフレームの基準に
sub esp, 4 ローカル変数確保 result用に4バイト確保([ebp-4])
push 4 addの第2引数 スタックに積む
push 3 addの第1引数 スタックに積む
call add add関数呼び出し リターンアドレスをpushしてジャンプ
add esp, 8 引数削除 pushした引数2つ分をスタックから削除
mov [ebp-4], eax resultに保存 addの戻り値をローカル変数resultに書き込む
mov eax, [ebp-4] return result 戻り値としてeaxにロード
pop ebp フレームポインタ復元 スタックを元に戻す
ret 呼び出し元に戻る スタック上の戻りアドレスにジャンプ

4. ポイントまとめ

  1. 引数 → スタック上で [ebp+8], [ebp+12], ...

  2. ローカル変数[ebp-4], [ebp-8], ...

  3. 戻り値eax に格納

  4. 関数呼び出し時のスタックの流れ

    • 引数を積む → callで戻りアドレスが積まれる → calleeがフレームを作る → 処理 → retで戻る

  5. フレームポインタ(EBP) はローカル変数・引数を固定オフセットで参照するための目印


💡 補足:

  • スタックは LIFO(後入れ先出し)

  • push で上に積む、pop で取り出す

  • esp は常に「スタックの一番上」、ebp は「フレームの基準」