
C言語基礎|インライン関数と番兵法
ここまでで、関数を自分で作って呼び出せるようになってきましたね。次のステップでは、「速く(なる可能性)と読みやすさ」を意識した書き方を学びます。
今回のテーマはこの2つです。
- インライン関数:小さな処理を関数にまとめつつ、関数呼出しの負担を減らせる可能性がある。
- 番兵法:探索ループの終了判定を減らし、ループを軽くする工夫

インライン関数とは
インライン関数は、関数定義の前に inline を付けた関数です。コンパイラが「この関数は小さいし、呼び出すより展開したほうがよさそう」と判断すると、呼び出し箇所に関数本体を埋め込むことがあります。
書式
inline 返却値型 関数名(仮引数型並び)
{
文;
}例(絶対値)
inline int iabs(int x)
{
return x < 0 ? -x : x;
}インライン関数の特徴
| 観点 | 内容 |
|---|---|
| ねらい | 小さい処理を関数化しつつ、速くなる可能性を残す。 |
| 注意 | inline を付けても必ず展開されるわけではない。 |
| 使いどころ | 何度も呼ぶ短い関数、条件演算子などで完結する処理 |
番兵法とは
線形探索では、ループのたびに「終端か?」と「一致か?」のように 複数の判定が入りがちです。
番兵法では、配列の末尾に 番兵(sentinel) を置いて、
- 「必ずどこかで key が見つかる」状態を作る。
- ループ中の判定を v[i] == key だけにする。
という形にします。

通常探索と番兵法
| 方法 | ループ中の判定 | 特徴 |
|---|---|---|
| 通常の線形探索 | 終端判定 + 一致判定 | 判定が2つになりやすい |
| 番兵法 | 一致判定だけ | ループがシンプルになりやすい |
サンプルプログラム
題材を「社員IDを探す」に変更し、表示メッセージも差し替えます。
プロジェクト名:chap6-17-1 ソースファイル名:chap6-17-1.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
// 逐次探索(番兵法):社員IDを探す
#include <stdio.h>
#define SIZE 6
#define NOT_FOUND -1
//--- 要素数nの配列vからkeyを探索(番兵法) ---//
int search_sentinel(int v[], int n, int key)
{
int i = 0;
v[n] = key; // 番兵を格納(書き込みがあるのでconstは付けない)
while (v[i] != key) { // 判定は1つだけ
i++;
}
return i < n ? i : NOT_FOUND;
}
int main(void)
{
int ids[SIZE + 1];
int key;
puts("社員IDを入力してください。");
for (int i = 0; i < SIZE; i++) {
printf("ids[%d]:", i);
scanf("%d", &ids[i]);
}
printf("探したい社員ID:");
scanf("%d", &key);
int idx = search_sentinel(ids, SIZE, key);
if (idx == NOT_FOUND)
puts("該当するIDはありません。");
else
printf("見つかりました:ID %d は ids[%d] にあります。\n", key, idx);
return 0;
}なぜ const が外れるの?
番兵法は v[n] = key のように 配列へ書き込むので、引数を const int v[] にすると代入できずエラーになります。
const を付ける判断
| 関数の役割 | 例 | const を付ける? |
|---|---|---|
| 読むだけ | 最大/最小、合計、表示 | 付けるのが基本 |
| 書き換える | 反転、代入、番兵を置く | 付けない |
演習問題
演習6-8:平均との差の最大を返す
要素数 n の int 配列 v の各要素について、平均との差(平均との差の絶対値)を計算し、
その最大値を返す関数を作成せよ。
int max_dev(const int v[], int n);例:v が {10, 20, 30} のとき平均は 20、差は {10, 0, 10} なので返却値は 10。
解答例
プロジェクト名:chap6-17-2 ソースファイル名:chap6-17-2.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
#include <stdio.h>
inline int iabs(int x) { return x < 0 ? -x : x; }
int max_dev(const int v[], int n)
{
int sum = 0;
for (int i = 0; i < n; i++) sum += v[i];
int avg = sum / n; // int平均(小数は切り捨て)
int max = iabs(v[0] - avg);
for (int i = 1; i < n; i++) {
int d = iabs(v[i] - avg);
if (d > max) max = d;
}
return max;
}
int main(void)
{
int a[] = {10, 20, 33, 18, 19};
printf("平均との差の最大=%d\n", max_dev(a, 5));
return 0;
}解説
- max_dev は 配列を読むだけなので const int v[] にするのが安心です。
- inline の iabs は短い処理なので、可読性アップと最適化の可能性を両取りできます。
- 平均を int で計算しているので、小数は切り捨てになります(学習段階ではこれでOK)。
演習6-9:左回転する
要素数 n の int 配列 v を、左に k 個回転させる関数を作成せよ。
例:{1,2,3,4,5} を k=2 左回転 → {3,4,5,1,2}
void rot_left(int v[], int n, int k);解答例
プロジェクト名:chap6-17-3 ソースファイル名:chap6-17-3.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
#include <stdio.h>
void rot_left(int v[], int n, int k)
{
if (n <= 0) return;
k %= n;
if (k < 0) k += n;
while (k-- > 0) {
int first = v[0];
for (int i = 0; i < n - 1; i++)
v[i] = v[i + 1];
v[n - 1] = first;
}
}
void print_array(const int v[], int n)
{
printf("{ ");
for (int i = 0; i < n; i++) printf("%d ", v[i]);
printf("}\n");
}
int main(void)
{
int a[] = {1, 2, 3, 4, 5};
print_array(a, 5);
rot_left(a, 5, 2);
puts("左に2回転させました。");
print_array(a, 5);
return 0;
}解説
- rot_left は 配列を書き換えるので const は付けません。
- 回転は「1回左へずらす」を k 回繰り返すだけでも実装できます(まずはシンプルに)。
- n が 0 のときや k が大きいときのために k %= n を入れておくと安心です。
演習6-10:フィルタコピーする
要素数 n の int 配列 src から、偶数だけを dst に順に詰めて格納する関数を作成せよ。
返却値は、dst に格納した個数とする。
int copy_even(int dst[], const int src[], int n);解答例
プロジェクト名:chap6-17-4 ソースファイル名:chap6-17-4.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
#include <stdio.h>
int copy_even(int dst[], const int src[], int n)
{
int cnt = 0;
for (int i = 0; i < n; i++) {
if (src[i] % 2 == 0) {
dst[cnt++] = src[i];
}
}
return cnt;
}
int main(void)
{
int src[] = {7, 4, 9, 2, 2, 5, 8};
int dst[7];
int m = copy_even(dst, src, 7);
printf("偶数だけを%d個コピーしました。\n", m);
for (int i = 0; i < m; i++)
printf("dst[%d]=%d\n", i, dst[i]);
return 0;
}解説
- src は読み取り専用なので const int src[] がぴったりです。
- dst は書き込み先なので const は付けません。
- 「配列を受け渡すときは、処理対象の個数 n を別に渡す」が基本形です。
演習6-11:条件を満たす添字列挙
要素数 n の int 配列 v の中から、threshold(しきい値) 以上の要素の添字を idx に格納する関数を作成せよ。
返却値は格納した添字の個数とする。
int collect_ge(const int v[], int idx[], int threshold, int n);解答例
プロジェクト名:chap6-17-5 ソースファイル名:chap6-17-5.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
#include <stdio.h>
int collect_ge(const int v[], int idx[], int threshold, int n)
{
int cnt = 0;
for (int i = 0; i < n; i++) {
if (v[i] >= threshold) {
idx[cnt++] = i;
}
}
return cnt;
}
int main(void)
{
int v[] = {5, 12, 7, 20, 10};
int idx[5];
int c = collect_ge(v, idx, 10, 5);
printf("条件を満たす要素は%d個ありました。\n", c);
for (int i = 0; i < c; i++)
printf("idx[%d]=%d (v[%d]=%d)\n", i, idx[i], idx[i], v[idx[i]]);
return 0;
}解説
- 「見つけた位置を idx 配列に詰める」方式は、6-11系の王道パターンです。
- idx のサイズは最大で n 個必要なので、呼び出し側は idx を n 以上で確保しておくのが安全です。
- v は読むだけなので const を付けると「書き換えない」意思が明確になります。
