関数の基礎

関数は台所のレシピのようなものです——材料(仮引数)を渡せば、手順に従って調理し、料理(戻り値)を出してくれます。繰り返し使う論理を関数にまとめれば、コードはすっきりと効率的になります。

関数の定義

関数定義は4つの部分から構成されます。戻り値の型、関数名、仮引数リスト、関数本体です。

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

仮引数がない場合は、仮引数リストにvoidと書きます。

C
int get_value(void) {
    return 42;
}

関数の呼び出し

関数を呼び出すには、関数名に続けて丸括弧を書き、実引数を渡します。

C
int result = add(3, 5);
printf("%d\n", result);

呼び出されると、プログラムは関数本体にジャンプし、returnに遭遇するか関数の末尾に達すると呼び出し元に戻ります。

関数はネストできます。ある関数の戻り値を別の関数の実引数にできます。

C
int x = add(add(1, 2), add(3, 4));

これはadd(3, 7)と等価で、結果は10です。

仮引数と戻り値

仮引数と実引数

関数定義の丸括弧内の変数を仮引数(仮パラメータ)と呼びます。呼び出し時に渡す値を実引数(実パラメータ)と呼びます。

C
int square(int n) {
    return n * n;
}

int main(void) {
    int x = 5;
    int y = square(x);
    return 0;
}

nは仮引数、xは実引数です。仮引数は関数内の局所変数であり、実引数の値は仮引数にコピーされます。これが「値渡し」です。関数内で仮引数を変更しても実引数には影響しません。

C
void try_change(int n) {
    n = 100;
}

int main(void) {
    int x = 5;
    try_change(x);
    printf("%d\n", x);
    return 0;
}

出力は5のままです。nxのコピーに過ぎないからです。

戻り値

関数はreturn文を通じて結果を呼び出し元に返します。戻り値の型は関数宣言と一致している必要があります。

C
double circle_area(double r) {
    return 3.14159 * r * r;
}

関数は変数に格納せず、式を直接返すこともできます。returnが実行されると関数はただちに終了し、以降のコードは実行されません。

C
int max(int a, int b) {
    if (a > b) {
        return a;
    }
    return b;
}

関数の宣言(プロトタイプ)

関数が呼び出し位置より後で定義されている場合、コンパイラは関数名を認識できず、エラーを出すか暗黙にintを返すと仮定します。解決策は、呼び出しの前に関数プロトタイプを宣言することです。

C
int add(int a, int b);

int main(void) {
    int result = add(3, 5);
    return 0;
}

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

宣言はヘッダーに続けてセミコロンを書くだけで、関数本体は不要です。仮引数名は省略可能です。

C
int add(int, int);

ただし、仮引数名を残しておくほうが可読性が高く、推奨されます。

💡 ヒント: 実際のプロジェクトでは、関数宣言はヘッダファイル(.h)に、関数定義はソースファイル(.c)に配置するのが一般的です。

void関数

戻り値が不要な関数は、戻り値の型にvoidを使います。

C
void print_line(int len) {
    int i;
    for (i = 0; i < len; i++) {
        printf("-");
    }
    printf("\n");
}

void関数ではreturnは省略可能です。また、return;で早期に抜けることもできます。

C
void greet(int hour) {
    if (hour < 0 || hour > 23) {
        return;
    }
    if (hour < 12) {
        printf("Good morning\n");
    } else {
        printf("Good afternoon\n");
    }
}
⚠️ 注意: void関数は値を返せません。return 5;はコンパイルエラーになります。同様に、void以外の関数ではすべての実行パスに正しい型のreturnが必要です。

整数が偶数かどうかを判定します。

C
#include <stdio.h>

int is_even(int n) {
    return n % 2 == 0;
}

int main(void) {
    int nums[] = {3, 4, 7, 8, 11, 16};
    int i;
    int len = sizeof(nums) / sizeof(nums[0]);

    for (i = 0; i < len; i++) {
        printf("%d is %s\n", nums[i], is_even(nums[i]) ? "even" : "odd");
    }

    return 0;
}
▶ 試してみよう
TEXT
3 is odd
4 is even
7 is odd
8 is even
11 is odd
16 is even

return文の詳細

関数は複数のreturn文を持てますが、1回の呼び出しで実行されるのは1つだけです。returnには2つの効果があります。

  1. 呼び出し元に値を返す
  2. 関数の実行をただちに終了する
C
const char *grade(int score) {
    if (score >= 90) return "Excellent";
    if (score >= 80) return "Good";
    if (score >= 60) return "Pass";
    return "Fail";
}

戻り値は呼び出し元にコピーされます。基本型(int、doubleなど)ではコピーのオーバーヘッドは無視できます。関数が返った後、その局所変数はもう存在しないため、局所変数へのポインタを返してはいけません。

複数仮引数の関数

関数はカンマで区切って複数の仮引数を持てます。実引数の数と型は仮引数と正確に一致している必要があります。

C
double bmi(double weight, double height) {
    return weight / (height * height);
}

実引数は位置で仮引数に対応付けられます。

C
double result = bmi(70.0, 1.75);
⚠️ 注意: 実引数と仮引数の型が一致しない場合、コンパイラは暗黙の型変換を行うか警告を出す可能性があります。型は一致させるのが最善です。

2つの日付間の日数を計算します(簡易版。閏年や月の差は無視し、複数仮引数のデモに焦点を当てます)。

C
#include <stdio.h>

int day_of_year(int year, int month, int day) {
    int days_in_month[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    int total = 0;
    int m;

    for (m = 1; m < month; m++) {
        total += days_in_month[m];
    }
    total += day;

    return total;
}

int days_between(int y1, int m1, int d1, int y2, int m2, int d2) {
    int first = day_of_year(y1, m1, d1);
    int second = day_of_year(y2, m2, d2);
    int diff = second - first;
    if (diff < 0) diff = -diff;
    return diff;
}

int main(void) {
    int d = days_between(2025, 3, 1, 2025, 6, 15);
    printf("%d days apart\n", d);
    return 0;
}
▶ 試してみよう
TEXT
106 days apart

❓ よくある質問

Q 仮引数と実引数に同じ名前を使ってもよいですか?
A はい、異なるスコープの変数なので互いに影響しません。ただし混乱を避けるため、別の名前にすることを推奨します。
Q 関数の戻り値の型は省略できますか?
A C89では省略するとintが暗黙に仮定されます。C99以降は明示的な戻り値の型が必須です。常に戻り値の型を明示するのが良い習慣です。
Q void関数でreturn;の用途は何ですか?
A 関数を早期に抜けて以降の論理をスキップするために使います。ループのbreakに似ています。
Q 関数は複数の値を返せますか?
A Cの関数は1つの値しか返せません。複数の結果を返すには、ポインタの仮引数、構造体、配列を使ってください。

📖 まとめ

📝 練習問題

  1. 3つの整数のうち最大値を返す関数int max3(int a, int b, int c)を書いてください。
  2. 2つの値を交換しようとする関数void swap_wrong(int a, int b)を書き、mainで成功するか検証し、なぜ失敗するか説明してください。
  3. 正の整数の桁の合計を計算する関数int digit_sum(int n)を書いてください(例:123は6を返す)。mainでテストしてください。
100%