
C言語基礎|ポインタによる2値のソート
ポインタを“たらい回し”できれば勝ち!2値ソートで、関数とポインタの連携がスッと分かる。
2値の交換(swap)が作れるようになると、次にすぐ作れるのが 2値のソートです。
「小さいほうを前に、大きいほうを後ろに並べる」…やることは単純なんですが、ここにはポインタ学習の大事なポイントが詰まっています。
特に大事なのはこの2つです。
- 関数 sort2 は、呼び出し元の変数を変えたいので 住所(ポインタ) を受け取る。
- そのポインタを swap に渡すときは &を付けない(すでに住所だから)
この「&を付ける場面/付けない場面」をここでハッキリさせると、ポインタと関数の理解がかなり安定しますよ。

2値ソートとは(目的はこれだけ)
2値ソートは、「2つの値を昇順に並べる」ことです。
- n1 <= n2 なら、そのまま(すでにソート済み)
- n1 > n2 なら、入れ替える(swap)
2値ソートの判断
| 状態 | 条件 | やること |
|---|---|---|
| すでに昇順 | *n1 <= *n2 | 何もしない |
| 逆順 | *n1 > *n2 | swap(n1, n2) |
ここで注目ポイントは、比較しているのが n1 や n2 ではなく *n1 と *n2(指す先の値)というところです。
サンプルプログラム
赤チームの点数 red と 青チームの点数 blue を昇順にそろえるプログラム例です。
プロジェクト名:chap10-7-1 ソースファイル名:chap10-7-1.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
#include <stdio.h>
// 2値交換(pxとpyが指す先の値を交換)
void swap_int(int *px, int *py)
{
int temp = *px;
*px = *py;
*py = temp;
}
// *a <= *b となるように並べ替える(2値ソート)
void sort2_int(int *a, int *b)
{
if (*a > *b) {
swap_int(a, b); // ここは & を付けない
}
}
int main(void)
{
int red, blue;
puts("2つの点数を入力してください。");
printf("red = ");
scanf("%d", &red);
printf("blue = ");
scanf("%d", &blue);
sort2_int(&red, &blue); // ここは & が必要
puts("小さい順に並べました。");
printf("red = %d\n", red);
printf("blue = %d\n", blue);
return 0;
}ここが山場:& が必要な場所/不要な場所
この章のいちばん大事なところなので、表で一発整理します。
& を付けるかどうかの判断
| 場面 | 渡したいもの | 書き方 | なぜ? |
|---|---|---|---|
| main → sort2_int | red の住所、blue の住所 | sort2_int(&red, &blue) | sort2_int は住所が欲しい |
| sort2_int → swap_int | すでに持っている住所 | swap_int(a, b) | a と b はもう住所(ポインタ) |
| もし swap_int(&a, &b) と書いたら | a自身の住所、b自身の住所 | 誤り | 交換対象が red/blue ではなく a/b(ポインタ変数)になる |
ポインタの“たらい回し”とは(意味とメリット)
sort2_int の引数 a, b は、呼び出し元の変数の住所をコピーしたものです。
つまり sort2_int の中で a と b はこういう値を持っています。
- a の中身:red のアドレス
- b の中身:blue のアドレス
この「住所(アドレス)という情報」を swap_int にそのまま渡すのが、いわゆる たらい回しです。
図:住所が関数をまたいで受け渡される

sort2_int側:
a : [ &red ] b : [ &blue ]
if (*a > *b) なら
swap_int(a,b) を呼ぶ(住所をそのまま渡す)
swap_int側:
px: [ &red ] py: [ &blue ]
*px と *py を交換 → red と blue の中身が交換される
図の説明(読み方)
- 箱は変数(オブジェクト)
- a, b, px, py はポインタなので「住所」を入れる箱
- *a や *px は「住所の先の本人(値)」を表す。
- 住所を渡し続けることで、最終的に呼び出し元の値が更新される。
sort2_int の動作をステップで見る(*a と *b)
2値ソートは条件分岐が1つだけなので、流れはとてもシンプルです。
sort2_int の処理手順
| 手順 | 判定/処理 | 意味 |
|---|---|---|
| 1 | *a と *b を比較 | 指す先の値を比べる |
| 2 | *a > *b なら swap_int(a, b) | 逆順なら入れ替える |
| 3 | それ以外は何もしない | すでに昇順なので完了 |
よくあるミス:swap_int(&a, &b) と書いてしまう
これ、ポインタ初心者が高確率で踏むポイントです。
a と b は「red/blue の住所」を持つポインタ変数です。
&a を取ると「a 自身の住所」になってしまいます。つまり交換する対象が変わっちゃいます。
何の住所を渡している?
| 式 | 指しているもの |
|---|---|
| a | red の住所 |
| &red | red の住所(mainで必要) |
| &a | a というポインタ変数の住所(別物) |
登場する命令(関数)と書式・何をする?
sort2_int(自作関数)
- 書式:sort2_int(int *a, int *b)
- 何をする?:a と b が指す先の2つの int を昇順に並べる
- 中身:必要なら swap_int(a, b) を呼ぶ
swap_int(自作関数)
- 書式:swap_int(int *px, int *py)
- 何をする?:px と py が指す先の値を交換する
- 交換対象は px/py そのものではなく *px と *py
puts
- 書式:puts(文字列);
- 何をする?:文字列を表示して改行も付ける
printf
- 書式:printf(書式文字列, 引数1, 引数2, ...);
- 何をする?:書式に従って表示する
scanf
- 書式:scanf(書式文字列, 変数のアドレス, ...);
- 何をする?:入力を読み取り、指定した変数へ格納する
- ポイント:red に入力させるには &red のように住所が必要
使った表や図の説明(まとめ)
- &判断表は「今渡しているのは値か住所か」を見分けるための整理です。
- たらい回し図は「住所が関数間を移動しても、指す先は同じ」ことを見える化しています。
- 手順表は sort2_int が「比較して必要なら交換」だけだと分かるように簡略化しています。
- ミス表は &a を取ると「交換対象がズレる」落とし穴を防ぐための注意です。
演習問題
3値ソートは条件が増えて良い練習になります。ここでは「昇順に並べる」だけに集中します。
演習10-3:3つの値を昇順に並べる関数 sort3_int を作成せよ
void sort3_int(int *a, int *b, int *c);
解答例(swap_int と sort2_int を活用してOK)
プロジェクト名:chap10-7-1 ソースファイル名:chap10-7-1.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
#include <stdio.h>
void swap_int(int *px, int *py)
{
int temp = *px;
*px = *py;
*py = temp;
}
void sort2_int(int *a, int *b)
{
if (*a > *b) swap_int(a, b);
}
void sort3_int(int *a, int *b, int *c)
{
// まず a<=b、次に b<=c を作り、
// もう一度 a<=b を確認すると昇順になる
sort2_int(a, b);
sort2_int(b, c);
sort2_int(a, b);
}
int main(void)
{
int x, y, z;
puts("3つの整数を入力してください。");
printf("x = ");
scanf("%d", &x);
printf("y = ");
scanf("%d", &y);
printf("z = ");
scanf("%d", &z);
sort3_int(&x, &y, &z);
puts("昇順に並べました。");
printf("%d %d %d\n", x, y, z);
return 0;
}解説(なぜこれで昇順になる?)
- sort2_int(a,b) で a<=b を保証
- sort2_int(b,c) で b<=c を保証
- ただし b を動かした影響で a と b が逆転する可能性があるので、最後にもう一度 sort2_int(a,b)
- 小さな部品(2値ソート)を組み合わせて3値ソートにできるのも、関数とポインタの良さです。
