トラック7・🧩関数とスコープ

コールスタック——関数呼び出しの舞台裏

読む目安 約14分・ゴール:関数呼び出しでスタックフレームが積まれ、戻るときに降ろされる流れを説明できる

アプリでこのトピックを学ぶ(無料・登録不要)

このトピックの要点

関数を呼ぶと、なぜ元の場所に戻ってこられるのでしょうか。しかも、呼ばれた側のローカル変数はどこに置かれているのでしょうか。その舞台裏がコールスタックです。

食堂のお盆を想像してください。新しいお盆は必ずいちばん上に積み、取るときもいちばん上から取ります。関数の呼び出しもこれと同じ規則で動きます。

#include <stdio.h>

int square(int x)
{
    int r = x * x;
    return r;
}

int calc(int a)
{
    int s = square(a);
    return s + 1;
}

int main(void)
{
    int n = 3;
    int ans = calc(n);
    printf("%d\n", ans);
    return 0;
}

main → calc → square と3段積まれ、square が終わると上から順に降りてきます。降ろされたフレームの中身(x や r)は、そこで消えます。前のトピックの「ブロックを抜けると変数が消える」は、この仕組みの結果だったわけです。

下のビジュアライザで、フレームが積まれて降りる様子を1行ずつ確かめてください。

🧪 メモリの図解:この図はアプリ内で1行ずつ動かせます。アプリでこのトピックを開く

フレームに入っている「帰り道」

フレームの中でいちばん地味で、いちばん重要なのが戻り先の住所です。calc の中で square を呼ぶとき、「終わったら calc の続きに帰る」という情報も一緒に積まれます。これがあるから、同じ square を100か所から呼んでも、それぞれ正しい場所へ帰れます。関数が「行って帰ってこられる」のは、この一枚のメモのおかげです。

積みすぎると崩れる

#include <stdio.h>

void dig(int n)
{
    int buf[1000];       // 呼ぶたびに約4KBを積む
    printf("%d\n", n);
    dig(n + 1);          // 止まらない!
}

int main(void)
{
    dig(1);
    return 0;
}

このプログラムは数千回から数万回で異常終了します(回数は環境によって変わります)。return に到達しないので、フレームは積まれる一方で降りてきません。積む場所には上限があるため、いつか限界に達します。

罠・注意

ビット「行って、帰ってくる」の帰り道を覚えているのがフレーム。だから迷子にならないんだ

まとめ: 呼べば積み、戻れば降ろす。コールスタックはCの実行の背骨で、スコープ・寿命・再帰・ポインタの事故まで、すべてここに繋がります。

アプリでこのトピックを学ぶ(無料・登録不要)

確認問題

確認問題 1

関数が return して呼び出し元に戻るとき、その関数のスタックフレームはどうなりますか。

  1. そのまま残り続け、次に同じ関数を呼ぶと再利用される
  2. スタックの底に移動して保存される
  3. 取り除かれ、中のローカル変数も使えなくなる
  4. グローバル領域にコピーされる
答えを見る

正解:C(取り除かれ、中のローカル変数も使えなくなる)

コールスタックは後入れ先出しで、return するといちばん上のフレームが取り除かれます。フレームにあった引数やローカル変数はそこで消えます。「ブロックを抜けると変数が消える」のは、この仕組みの結果です。

確認問題 2

関数呼び出しの流れを追って、空欄を埋めてください(値がない欄は - と書かれています)。

#include <stdio.h>

int twice(int x)
{
    return x * 2;
}

int main(void)
{
    int a = 3;
    int b = twice(a);
    printf("%d\n", b);
    return 0;
}
行main:amain:btwice:x出力
103--
113-?
53-3
113?-
1236-?
答えを見る

空欄の答え(上から順に):3、6、6

11行目で twice(a) を呼ぶと、a の値 3 のコピーが仮引数 x に入ります。5行目で x * 2 = 6 が返り、twice のフレームは消えて b に 6 が入ります。12行目でその 6 が表示されます。

「関数とスコープ」の目次

  1. 関数とは——処理に名前を付ける
  2. 引数と戻り値
  3. プロトタイプ宣言
  4. 変数のスコープ——見える範囲
  5. 変数の寿命とstatic
  6. コールスタック——関数呼び出しの舞台裏
  7. 値渡しの本質
  8. グローバル変数との付き合い方
  9. 関数分割の設計センス
  10. 再帰の入口——自分を呼ぶ関数

もっと先へ:ポインタ・メモリ・ファイル入出力・セキュアコーディングを含む全26トラックと、段位検定・模試のフルセットは完全版に収録しています。

完全版の販売ページは準備中です。

ほかのトラック

📚 姉妹教材:手を動かして覚える Linux 教材 — Linuxとインフラの仕組みを地図で学ぶ

Web版