関数の基礎
関数は台所のレシピのようなものです——材料(仮引数)を渡せば、手順に従って調理し、料理(戻り値)を出してくれます。繰り返し使う論理を関数にまとめれば、コードはすっきりと効率的になります。
関数の定義
関数定義は4つの部分から構成されます。戻り値の型、関数名、仮引数リスト、関数本体です。
int add(int a, int b) {
return a + b;
}
int:戻り値の型。関数の結果の型を示すadd:関数名。呼び出し時に使う(int a, int b):仮引数リスト。呼び出し側からデータを受け取る{ return a + b; }:関数本体。実際の論理を実行する
仮引数がない場合は、仮引数リストにvoidと書きます。
int get_value(void) {
return 42;
}
関数の呼び出し
関数を呼び出すには、関数名に続けて丸括弧を書き、実引数を渡します。
int result = add(3, 5);
printf("%d\n", result);
呼び出されると、プログラムは関数本体にジャンプし、returnに遭遇するか関数の末尾に達すると呼び出し元に戻ります。
関数はネストできます。ある関数の戻り値を別の関数の実引数にできます。
int x = add(add(1, 2), add(3, 4));
これはadd(3, 7)と等価で、結果は10です。
仮引数と戻り値
仮引数と実引数
関数定義の丸括弧内の変数を仮引数(仮パラメータ)と呼びます。呼び出し時に渡す値を実引数(実パラメータ)と呼びます。
int square(int n) {
return n * n;
}
int main(void) {
int x = 5;
int y = square(x);
return 0;
}
nは仮引数、xは実引数です。仮引数は関数内の局所変数であり、実引数の値は仮引数にコピーされます。これが「値渡し」です。関数内で仮引数を変更しても実引数には影響しません。
void try_change(int n) {
n = 100;
}
int main(void) {
int x = 5;
try_change(x);
printf("%d\n", x);
return 0;
}
出力は5のままです。nはxのコピーに過ぎないからです。
戻り値
関数はreturn文を通じて結果を呼び出し元に返します。戻り値の型は関数宣言と一致している必要があります。
double circle_area(double r) {
return 3.14159 * r * r;
}
関数は変数に格納せず、式を直接返すこともできます。returnが実行されると関数はただちに終了し、以降のコードは実行されません。
int max(int a, int b) {
if (a > b) {
return a;
}
return b;
}
関数の宣言(プロトタイプ)
関数が呼び出し位置より後で定義されている場合、コンパイラは関数名を認識できず、エラーを出すか暗黙にintを返すと仮定します。解決策は、呼び出しの前に関数プロトタイプを宣言することです。
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;
}
宣言はヘッダーに続けてセミコロンを書くだけで、関数本体は不要です。仮引数名は省略可能です。
int add(int, int);
ただし、仮引数名を残しておくほうが可読性が高く、推奨されます。
void関数
戻り値が不要な関数は、戻り値の型にvoidを使います。
void print_line(int len) {
int i;
for (i = 0; i < len; i++) {
printf("-");
}
printf("\n");
}
void関数ではreturnは省略可能です。また、return;で早期に抜けることもできます。
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が必要です。
例
整数が偶数かどうかを判定します。
#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;
}
3 is odd
4 is even
7 is odd
8 is even
11 is odd
16 is even
return文の詳細
関数は複数のreturn文を持てますが、1回の呼び出しで実行されるのは1つだけです。returnには2つの効果があります。
- 呼び出し元に値を返す
- 関数の実行をただちに終了する
const char *grade(int score) {
if (score >= 90) return "Excellent";
if (score >= 80) return "Good";
if (score >= 60) return "Pass";
return "Fail";
}
戻り値は呼び出し元にコピーされます。基本型(int、doubleなど)ではコピーのオーバーヘッドは無視できます。関数が返った後、その局所変数はもう存在しないため、局所変数へのポインタを返してはいけません。
複数仮引数の関数
関数はカンマで区切って複数の仮引数を持てます。実引数の数と型は仮引数と正確に一致している必要があります。
double bmi(double weight, double height) {
return weight / (height * height);
}
実引数は位置で仮引数に対応付けられます。
double result = bmi(70.0, 1.75);
例
2つの日付間の日数を計算します(簡易版。閏年や月の差は無視し、複数仮引数のデモに焦点を当てます)。
#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;
}
106 days apart
❓ よくある質問
return;の用途は何ですか?breakに似ています。📖 まとめ
- 関数は戻り値の型、関数名、仮引数リスト、関数本体から構成されます
- 実引数は仮引数にコピーされます(値渡し)。関数内で仮引数を変更しても実引数には影響しません
- 関数は呼び出し前に宣言または定義されている必要があります。プロトタイプ宣言で十分です
- void関数は値を返しません。早期脱出には
return;を使います - return文は関数をただちに終了し、呼び出し元に値を返します
📝 練習問題
- 3つの整数のうち最大値を返す関数
int max3(int a, int b, int c)を書いてください。 - 2つの値を交換しようとする関数
void swap_wrong(int a, int b)を書き、mainで成功するか検証し、なぜ失敗するか説明してください。 - 正の整数の桁の合計を計算する関数
int digit_sum(int n)を書いてください(例:123は6を返す)。mainでテストしてください。



