qsort() 関数 〜配列の中身を、順番に並べ替えるには〜

目次に戻る

☆ qsort() 関数とは?−何に使う関数なのか−

 今、ここに12人の生徒がいるとします。
 そして手元には、この12人の生徒のテストの点数データがあります。

太郎 次郎 三郎 四郎 五郎 六郎 七郎 八郎 九太郎 十子 十一郎 十二郎
50 30 90 44 13 46 82 43 70 74 20 62

 先生は、この12人に順位を付けて、教室の掲示板に貼り出したいと考えています。
 しかし、このままでは、順位表を作れません。
 
 そんな時に、これらの点数を順番に並べ替えてくれるのが、qsort() 関数です。

 
☆ 具体的には

 qsort() は quick sort の略で、ソートとは数字や文字列を並べ替えることです。
 このqsort関数は、1つの配列中にある数字や文字を、順番に並べ替えてくれる関数です。
 
 例えば、

int Tensu[] = { 50 , 30 , 90 , 44 , 13 , 46 , 82 , 42 , 70 , 74 , 20 , 62 };

 となっているデータを、

int Tensu[] = { 13 , 20 , 30 , 42 , 44 , 46 , 50 , 62 , 70 , 74 , 82 , 90 };

 という風に並べ替えてくれます。
 それどころか、

int Tensu[] = { 90 , 82 , 74 , 70 , 62 , 50 , 46 , 44 , 42 , 30 , 20 , 13 };

 という風に、降順に並べ替えることもできます。
 もちろん、文字や小数、構造体の整列も可能です。

 
☆ qsort() 関数の使い方

 さて、そんな便利なqsortですが、1つ欠点があります。
 それは、使い方を覚えるのが、非常に難しいことです。
 
 このqsort() 関数の定義は、こうなっています。

void qsort ( void* base , size_t nmemb , size_t size, int(*compar)(const void *, const void *) )

 ワケが分かりません。
 

 いきなり void* とか言われても初心者には何の事やら分かりませんし、一番右の int(*compar)‥‥に至っては、
 何の事やらさっぱりです。
 
 しかし、そのまま使わずに、ほったらかしにするには非常に勿体ない関数です。
 そこで、この意味不明な定義を意訳してみると、こういう意味になります。

void qsort ( 並べ替えたい配列のアドレス , その配列のサイズ , sizeof(その配列の型) , int型の比較関数へのポインタ )

 例えば、int型の配列 Tensu を並べ替えるには、こう記述します。

qsort(Tensu , 12 , sizeof (int) , MyComp );

日本語訳: 大きさが12の、int型配列Tensuを並べ替えなさい。

 そして、もしも、大きさが100の配列 「 float Sincho[100] 」を並べ替えたいのであれば、

qsort(Sincho , 100 , sizeof (float) , MyComp );

 と記述すれば並べ替えてくれます。

 
☆ MyComp って何さ?

 ‥‥しかし、上記の文を、そのままコンパイラに通すと、エラーが出ます。
 なぜなら、関数 MyComp は、標準のライブラリ関数ではないからです。
 というか、引数に関数を使うこと自体、馴染みの無い方も多いかもしれません。
 そしてqsort関数が使いにくい理由は、ここにあります。

 実は、qsort()関数を使うには、自分で比較関数を作らなくてはいけません。

 どんな関数を作らなくちゃいけないのかと言うと、数字同士を比較する関数です。
 aとbの2つの数を比較して、aの方が大きければ正の数を返し、bの方が大きければ負の数を返す。
 そんな関数を自作しなければ、qsort()関数は動いてくれないのです。
 
 しかしそれは、逆に言えば、その関数を作るコツさえマスターすれば、どんな型のソートでも可能になるという事です。
 とりあえず、ここでは long 型の数値を比較する関数を作ってみることにしましょう。

int MyCmp(const void* a , const void* b)
{
 long *x, *y;

 x = (long*) a;
 y = (long*) b;
 
 return x - y;
}

 関数には必ず、const void* 型の引数を2つ用意する必要があります。
 そして、上記は long 型の比較を行いたい場合の関数ですが、もしも double 型の比較を行いたい場合は、
 以下のような関数を作ります。

int MyCmp(const void* a , const void* b)
{
 double *x, *y;

 x = (double*) a;
 y = (double*) b;
 
 return x - y;
}

 そして、文字の比較を行いたい場合(例えば、ACGBDFE となっているのを、ABCDEFG という風に並べ替える)には、
 上の関数の赤字の部分を、char に変えた関数を作って、qsort() に使わせることもできます。

 さらに、この関数を工夫すれば、こんな事もできます。

struct Seiseki {
 char Name[10];
 int Tensu;
};

Seiseki
Juni[12];

/* 構造体Juni[n]に、各生徒の名前と点数を代入する処理 (省略) */

qsort(Juni , 12 , sizeof(Seiseki ) , MyComp );

int
MyCmp(const void* a , const void* b)
{
 Seiseki *x, *y;

 x = (Seiseki*) a;
 y = (Seiseki*) b;
 
  return (x ->Tensu ) - (y -> Tensu);
}

 また、上の例では、「1、2、4、9、12‥‥」という順番(昇順)に並べ替えますが、これを2文字変えるだけで、
 逆の降順(12、9、4、2、1‥‥)に並べ替えることもできます。

int MyCmp(const void* a , const void* b)
{
 long *x, *y;

 x = (long*) a;
 y = (long*) b;
 
 return y - x;
}

 また、こうすることで、ランダムに並べ替えることもできます。

int MyCmp(const void* a , const void* b)
{ 
 return (rand()%2) ? 1 : -1 ;
}

 このように、qsort() 関数は、覚えるのは難しいですが、使いこなせば使いこなすほど便利に使える、
 非常に奥の深い関数です。

(覚えにくいけど使えば使うほどクセになる‥‥なんかC言語みたい;)

 
☆ 補足

 ・関数MyCmpには、const void 型の引数を2つ用意する必要がありますが、qsort()文を使うときには、
  MyCmpと入力するだけで十分です。引数は必要ありません。qsort()関数が、勝手に引数を当てはめてくれます。

 ・数字や文字をソートする方法は、このクイックソートの他にもバブルソートや挿入法など、色んな方法があります。
  が、データの数が多いときには、基本的にこのクイックソートが、一番速いと言われています。

 ・しかし、クイックソートは「順序どおりに並んでいるデータ」に非常に弱いです。
  たとえば、「1、2、3、4、5‥‥1000万、1000万1、1000万2‥‥」というデータの場合、滅茶苦茶遅くなります。
  逆順の場合は、さらに遅くなります。

☆ このページの要約−結局、何が言いたいのか?−

 ・関数qsort()は、配列変数の中身を、指定したとおりに並べ替えてくれる関数である。

 ・関数qsort()を使うには、まず自分で関数を作る必要がある。
  それは、以下のような関数である。

int MyCmp(const void* a , const void* b)
{
 long *x, *y;

 x = (long*) a;
 y = (long*) b;
 
 return x - y;
}

 上記は、long型の配列を並べ替えたい場合の関数である。int型を並べ替えたい場合は赤字の部分をintに、
 char型を並べ替えたい場合は、赤字の部分をcharに変えれば良い。

 ・以上のような自作関数を用意した上で、以下の文を打ち込めば、qsort()は正常に動いてくれる。

qsort( Kingaku , 100 , sizeof (long) , MyComp );

日本語訳: 大きさが 100 の、long 型配列 Kingaku を並べ替えなさい。

 ・なお、MyCompの工夫次第では、逆順に並べ替えたり、構造体配列を並べ替えたりすることもできる。


目次に戻る