C言語基礎|関数呼出しの仕組み

呼び出した瞬間、プログラムは“別の部屋”へ移動する

関数定義(関数を作る)が見えてきたら、次は 関数呼出し(関数を使う) です。
関数呼出しは一言でいうと、プログラムの流れが一時停止して、別の処理にジャンプして、結果を持って戻ってくる仕組みです。

最初は「なんか呼んだら戻ってくる」くらいの感覚でOK。
でも、ここをしっかり理解すると、デバッグも設計も一気にラクになります。

今回のゴール:関数呼出しで何が起きているかを言葉にできる

関数呼出しで起きることは、大きく3つです。

  1. 呼び出し元(mainなど)の実行が一時停止する。
  2. 呼び出された関数側で仮引数が用意され、実引数の値が代入される。
  3. return で値(または終了)を持って呼び出し元へ戻る。

この流れを図と表で整理しながら、体感できる例に差し替えて解説します。

サンプルプログラム

2つの整数の差の絶対値を返す関数 absdiff を例に解説をします。

プログラム例:absdiff を呼び出して表示する

プロジェクト名:chap6-2-1 ソースファイル名:chap6-2-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

// 2つの整数の差の絶対値を返す
int absdiff(int a, int b)
{
    int d = a - b;
    if (d < 0)
        d = -d;
    return d;
}

int main(void)
{
    int n1, n2;

    puts("2つの整数を入力してください。差の絶対値を表示します。");
    printf("1つ目:");
    scanf("%d", &n1);
    printf("2つ目:");
    scanf("%d", &n2);

    printf("差の絶対値は %d です。\n", absdiff(n1, n2));

    return 0;
}

実行例

2つの整数を入力してください。差の絶対値を表示します。
1つ目:45
2つ目:83
差の絶対値は 38 です。

まず大事:プログラム開始時に動くのは main

ここ、地味に大事です。

  • プログラムが起動したら main が実行される
  • main より上に関数が書いてあっても、勝手に先に実行されたりしない
  • 実行されるのは 呼び出したときだけ
よくある誤解実際
上に書いた関数が先に動きそう呼ばれるまで動かない
関数は置いた瞬間に実行される関数呼出し式が評価されたときに実行される

関数呼出し式とは:absdiff(n1, n2) は「依頼書」

呼び出しはこの形でしたね。

  • absdiff(n1, n2)

この ( ) が 関数呼出し演算子 です。
だから、absdiff(n1, n2) は 関数呼出し式(式)になります。

依頼として読むと超わかりやすい

関数 absdiff さん、n1 と n2 の値を渡すので、差の絶対値を返してください!

実引数と仮引数:値が「コピー」されて渡る(基本)

関数呼出しが起きると、呼び出し元で与えた 実引数の値が、関数側の 仮引数へ代入されます。

用語どこにいる?今回の例
実引数呼び出す側(main側)n1, n2
仮引数呼び出される側(関数側)a, b

呼出し直後に起きること(イメージ)

呼び出し元呼び出される関数
absdiff(n1, n2)int absdiff(int a, int b)
n1 の値が渡されるa に代入される
n2 の値が渡されるb に代入される

「まるでこう書いたみたいに」と考えると理解が早いです。

  • a = n1;
  • b = n2;

※この「代入」は概念としての理解です(実際はコンパイラとABIがやってくれます)。

プログラムの流れ:呼び出した瞬間にジャンプして、戻ってくる

関数呼出しが行われると、main の実行は途中で止まり、関数へ移動します。

制御の流れ(すごく大事)

タイミングプログラムの流れ
呼び出し前main が実行中
absdiff(n1, n2) を評価main が一時停止し、absdiff が実行開始
return に到達absdiff を終了し、main の呼び出し地点へ戻る
戻った後main の続きが再開される

重要ポイント

  • 「main → 関数へ移動 → return で戻る」という 往復 が起きる
  • これが関数呼出しの基本の動き

return 文:関数を終えて「手みやげ(返却値)」を渡す

return は 関数の実行を終了して、呼び出し元へ戻る命令です。
さらに、return 式; の形なら 値も返します

return 文の書式

書式意味
return 式;関数を終了し、式の値を返す
return;関数を終了する(void のとき)

今回の absdiff は int を返す関数なので、最後はこれです。

return d;

超重要:返却値は「関数呼出し式の値」になる

ここが気持ちいいポイントです。

  • absdiff(n1, n2) は “式”
  • だから評価すると “値” が得られる
  • その値は 関数が return した値

つまり、

printf("差の絶対値は %d です。\n", absdiff(n1, n2));

この中の absdiff(n1, n2) を評価した値が、printf に渡されます。

printf へ値が渡る流れ

ステップ起きること
1absdiff(n1, n2) を評価する必要がある。
2main が止まり、absdiff が動く。
3return で値が返る。
4返った値が absdiff(n1, n2) の評価結果になる。
5printf がその値を %d に埋めて表示する。

関数呼出し演算子のまとめ

意味
x(args)関数 x を実引数 args で呼び出す(args は0個以上、複数ならコンマ区切り)
戻り値が void 以外x が返した値が、呼出し式の評価結果として得られる
戻り値が void値は返らない(呼出し式として値を生成しない)

実引数は変数だけじゃない:定数でも式でもOK

次も普通にOKです。

absdiff(n1, 5)
absdiff(n1 + 10, n2 * 2)

実引数=値が出せるもの だと思うとスッキリします。

この記事で使った表・図は何のため?

  • 実引数/仮引数の表:誰がどこで使われているか混乱しやすいので整理するため
  • 制御の流れの表:main が止まって関数へ移動し、戻る…を言語化するため
  • printf へ渡る流れの表:関数呼出し式の評価結果がそのまま引数になる点を可視化するため
  • 関数呼出し演算子まとめ表:定義を短く暗記できる形にするため