C言語で安全かつ読みやすいプログラムを書くためには、関数の定義と呼び出しの基本を丁寧に押さえることが近道です。
本記事では、関数の構成要素、定義の書き方、呼び出しのポイントを順を追って解説し、最後に実行可能なサンプルを示します。
戻り値や引数の型を正しく扱うことを中心に、初心者がつまずきやすい注意点も補足します。
C言語の関数の基本
C言語の関数とは
関数とは、ある処理をひとまとまりにして名前を付け、必要に応じて呼び出せるようにした部品です。
入力(引数)を受け取り、結果(戻り値)を返すことで、同じ処理を何度でも再利用できます。
C言語には標準ライブラリの関数に加えて、プログラマが自分で作るユーザー定義関数があります。
関数の構成(戻り値/関数名/引数/本体)
関数は次の4要素で構成されます。
行頭の型が戻り値の型、続く識別子が関数名、丸括弧内が引数リスト、波括弧の中が本体です。
戻り値の型 関数名(引数リスト) {
本体
}
以下の表に役割を整理します。
構成要素 | 役割 | 記述例 |
---|---|---|
戻り値の型 | 関数が返す値の型を表します | int 、void 、double |
関数名 | 呼び出し時に使う識別子です | add 、print_line |
引数リスト | 呼び出し時に渡す値の型と名前です | int a, int b 、void (引数なし) |
本体 | 実際の処理を書きます | { /* 手続き */ return a + b; } |
戻り値がない関数は型にvoid
を使います。
引数がない場合もvoid
と書きます。
main関数とユーザー定義関数
Cプログラムはmain
関数から実行が始まります。
ユーザー定義関数はmain
から呼び出される側で、main
の前に定義するか、少なくともプロトタイプ宣言を前に置いておく必要があります。
本記事では単一ファイル内で「使う前に定義する」方針を基本に解説します(複数ファイルやヘッダの分割は別記事で扱います)。
関数の定義の書き方
基本構文(関数定義)
関数定義のひな型を示します。
コメントで各部の意味を確認してください。
// 戻り値の型 関数名(引数の型 引数名, ...) { 本体 }
int add(int x, int y) { // 戻り値: int, 関数名: add, 引数: xとyはint
int sum = x + y; // 本体: 計算処理
return sum; // 戻り値を返す
}
void print_line(void) { // 戻り値なし(void)、引数なし(void)
// 本体: 1行の飾り線を表示するだけ
puts("---------------");
}
戻り値を返す関数ではreturn
文が必須です。
戻り値がvoid
の関数ではreturn;
を省略可能ですが、早期終了したい場合は書けます。
戻り値ありの定義例(int)
2つの整数を足し算するadd
関数の定義例です。
// 2つのintを受け取り、その和をintで返す
int add(int a, int b) {
return a + b; // 計算結果を呼び出し元へ返す
}
計算結果を変数に格納しても、直接return a + b;
としても構いません。
戻り値なしの定義例(void)
メッセージだけを表示するvoid
関数の例です。
// 画面に挨拶を表示するだけの手続き
void greet(const char *name) {
// printfはフォーマット済み出力を行う標準ライブラリ関数
printf("Hello, %s!\n", name);
}
副作用(表示や書き込み)が目的の関数ではvoid
がよく使われます。
使う前に定義する(単一ファイル)
Cでは関数を呼び出す箇所より前に、定義またはプロトタイプ宣言が必要です。
単一ファイルでは、もっとも簡単なのは「関数定義をmain
より前に置く」方法です。
古いC規格のように暗黙宣言に頼る書き方はエラーや未定義動作の原因になるため厳禁です。
#include <stdio.h>
// 呼び出しより前に定義するのでプロトタイプ宣言は不要
int add(int a, int b) {
return a + b;
}
int main(void) {
printf("%d\n", add(2, 3)); // OK: すでにaddが定義済み
return 0;
}
もし意図があってmain
より後に定義したい場合は、前方にプロトタイプ宣言を書きます(複数ファイルへの分割は別記事で解説します)。
#include <stdio.h>
int add(int a, int b); // プロトタイプ宣言(戻り値と引数型の約束)
int main(void) {
printf("%d\n", add(2, 3)); // 宣言があるのでOK
return 0;
}
int add(int a, int b) { // 実体定義は後ろでもよい
return a + b;
}
関数の呼び出しの書き方
基本構文(関数呼び出し)
呼び出しは関数名(実引数,...)
と書きます。
戻り値がある場合は代入や式に組み込みます。
- 代入例:
int s = add(10, 20);
- 式の一部:
printf("%d\n", add(1, 2) * 3);
- 手続き呼び出し:
greet("Alice");
戻り値を受け取る呼び出し
戻り値がある関数は、型の合う変数に受け取るのが基本です。
int s = add(3, 5); // sは8
printf("sum = %d\n", s);
戻り値を無視する呼び出し
戻り値がある関数でも、結果を使わない場合は代入せずに呼び出せます。
ただし静的解析では警告対象になることがあります。
意図を明確にするなら(void)関数呼び出し
と明示することがあります。
(void)add(100, 200); // 結果を意図的に無視している明示
戻り値がvoid
の関数はそもそも代入できません。
引数の数と型を合わせる
呼び出し時の実引数は、宣言された引数の数と型に一致させます。
数が足りない・多い、型が不一致といった誤りは未定義動作やバグの原因です。
プロトタイプ宣言が前にあれば、コンパイル時に型チェックされます。
数値リテラルの型や整数から浮動小数への暗黙変換に注意しましょう。
警告は必ずゼロにしてください。
繰り返し呼び出す
同じ関数をループ内で何度でも呼び出せます。
小さな関数に分けると、繰り返し処理の見通しが良くなります。
for (int i = 0; i < 3; i++) {
greet("C learner"); // 毎回同じ処理を呼ぶ
}
サンプルコードと実行例
例1 add関数を定義して呼び出す
2つの整数を足し算して表示するプログラムです。
定義をmain
の前に置く基本パターンにしています。
#include <stdio.h>
// 2つの整数a, bの和を返す
int add(int a, int b) {
// 中間変数を使うとデバッグしやすい
int sum = a + b;
return sum; // sumを呼び出し元へ返す
}
int main(void) {
int x = 12;
int y = 30;
// 戻り値を受け取って表示
int s = add(x, y);
printf("add(%d, %d) = %d\n", x, y, s);
// 戻り値を式の中で直接使う
printf("add(7, 5) * 2 = %d\n", add(7, 5) * 2);
return 0; // 正常終了
}
add(12, 30) = 42
add(7, 5) * 2 = 24
例2 void関数を呼び出す
表示専用のvoid
関数を定義して、複数回呼び出します。
#include <stdio.h>
// 飾り線を表示するだけの手続き
void print_line(void) {
puts("=========");
}
// 名前付きで挨拶する手続き
void greet(const char *name) {
printf("Hello, %s!\n", name);
}
int main(void) {
print_line(); // 1回目
greet("Alice"); // メッセージ表示
print_line(); // 2回目
greet("Bob");
print_line(); // 3回目
return 0;
}
=========
Hello, Alice!
=========
Hello, Bob!
=========
コンパイルと実行(gcc)
GNU Compiler Collection(gcc)を用いた基本的な手順です。
警告を有効にしてコンパイルする習慣を付けましょう。
- 例1の保存ファイル名を
example_add.c
とした場合 - 例2の保存ファイル名を
example_void.c
とした場合
gcc -std=c17 -Wall -Wextra -Wpedantic -o example_add example_add.c
./example_add
gcc -std=c17 -Wall -Wextra -Wpedantic -o example_void example_void.c
./example_void
-std=c17
はC17規格を使う指定、-Wall -Wextra -Wpedantic
は代表的な警告を有効にするオプションです。
上記のコマンドを実行すると次のような出力になります。
実行環境の差はほぼ影響しません。
# example_add の実行結果
add(12, 30) = 42
add(7, 5) * 2 = 24
# example_void の実行結果
=========
Hello, Alice!
=========
Hello, Bob!
=========
各行は、呼び出した関数の戻り値や副作用(表示)がそのまま反映されたものです。
戻り値がある関数は値として使える一方、void関数は表示などの副作用に意味があることを確認できます。
まとめ
本記事では、C言語における関数の定義と呼び出しの基本を解説しました。
関数は戻り値の型・関数名・引数・本体から成り、単一ファイルでは呼び出す前に定義するのが最も簡単で安全です。
戻り値は型の合う変数で受け取り、不要なら(void)
で意図を示すこともできます。
引数の数と型は宣言と一致させ、-Wall
などの警告を有効にしてエラーを未然に防ぎましょう。
実務でも学習でも、同じ処理を関数に切り出して再利用することで、コードの見通しと品質が大きく向上します。
複数ファイルへの分割やプロトタイプ宣言の管理は、次のステップとして学ぶと理解が深まります。