【C言語】
qsortで構造体や文字列を効率的にソートする方法

qsortの基本

■整数編

●第1引数 ソート対象の配列

int     配列[] = {  3,1,4,1,5,9,2,6,5,3  };  

●第2引数 配列の要素数

size_t 	要素数 = sizeof(配列) / sizeof(配列[0]) ;

●第3引数 配列1個のサイズ

sizeof(配列[0])

●第4引数 比較関数

int 比較関数(const void *p1, const void *p2) {
	int n1 = *(const int *)p1;
	int n2 = *(const int *)p2;
	return n1 - n2;
}

●整数編qsort使用例

#include <stdio.h>
#include <stdlib.h>
int 比較関数(const void *p1, const void *p2) {
	int n1 = *(const int *)p1;
	int n2 = *(const int *)p2;
	return n1 - n2;
}
//整数ソート
int main(void){
    int     配列[] = {  3,1,4,1,5,9,2,6,5,3  };  

    size_t 	要素数 = sizeof(配列) / sizeof(配列[0]) ;
    
    for (size_t i = 0 ; i < 要素数 ; i++){
        printf("ソート前:%d\n", 配列[i]);	
    }
    
    qsort(配列,要素数,sizeof(配列[0]),比較関数);
    
    for (size_t i = 0 ; i < 要素数 ; i++){
        printf("ソート後:%d\n", 配列[i]);	
    }
}

n1 – n2の引き算の結果で
数値順にソートします。

●qsort実行結果

./a.out
ソート前:3
ソート前:1
ソート前:4
ソート前:1
ソート前:5
ソート前:9
ソート前:2
ソート前:6
ソート前:5
ソート前:3
ソート後:1
ソート後:1
ソート後:2
ソート後:3
ソート後:3
ソート後:4
ソート後:5
ソート後:5
ソート後:6
ソート後:9

文字列編

第1引数:ソート対象の配列

    const char *配列[]={
        "moshi",
        "moshi",
        "kame",
        "yo", 
        "kamesan",
        "yo",
        "sekai",
        "no",
        "uchini"
    };

■第2引数:配列の要素数

size_t 要素数 = sizeof(配列) / sizeof(配列[0]) ;

第3引数:配列1個のサイズ

sizeof(配列[0])

●第4引数:比較関数

int 比較関数(
        const void *argv1,
        const void *argv2)
{
    const char *s1 = ((char *const*)argv1)[0];
    const char *s2 = ((char *const*)argv2)[0];
    return 	strcmp(s1,s2) ;
}

strcmp()で辞書並びにソートします。


●文字列編qsort使用例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int 比較関数(
        const void *argv1,
        const void *argv2)
{
    const char *s1 = ((char *const*)argv1)[0];
    const char *s2 = ((char *const*)argv2)[0];
    return 	strcmp(s1,s2) ;
}
//文字列ソート
int main(void){
    const char *配列[]={
        "moshi",
        "moshi",
        "kame",
        "yo", 
        "kamesan",
        "yo",
        "sekai",
        "no",
        "uchini"
    };
    size_t 要素数 = sizeof(配列) / sizeof(配列[0]) ;
    
    for (size_t i=0 ; i < 要素数; i++)
        printf("ソート前△:%s\n", 配列[i]);	

    qsort(配列,要素数,sizeof(配列[0]),比較関数);

    for (size_t i=0 ; i < 要素数; i++)
        printf("ソート後▼:%s\n", 配列[i]);	
}

●文字列編実行結果

./a.out
ソート前△:moshi
ソート前△:moshi
ソート前△:kame
ソート前△:yo
ソート前△:kamesan
ソート前△:yo
ソート前△:sekai
ソート前△:no
ソート前△:uchini
ソート後▼:kame
ソート後▼:kamesan
ソート後▼:moshi
ソート後▼:moshi
ソート後▼:no
ソート後▼:sekai
ソート後▼:uchini
ソート後▼:yo
ソート後▼:yo

■構造体編

qsortのソートキーが複数ある場合の使い方

●第1引数 ソート対象の配列

typedef struct {
    char    名前[128];
    int     年齢;
    char    郵便番号[128];
} 構造体枠_t ;
static 構造体枠_t 配列[]={
    {.名前="佐藤",.年齢=98,   .郵便番号="111-2345"},
    {.名前="鈴木",.年齢=108,  .郵便番号="222-3456"},
    {.名前="田中",.年齢=88,   .郵便番号="333-4567"},
    {.名前="加藤",.年齢=78,   .郵便番号="111-2345"},
    {.名前="伊藤",.年齢=78,   .郵便番号="333-4567"},
    {.名前="武藤",.年齢=78,   .郵便番号="222-3456"},
    {.名前="須藤",.年齢=78,   .郵便番号="111-2345"},
};

第2引数 配列の要素数

size_t 	要素数 = sizeof(配列) / sizeof(配列[0]) ;

第3引数 配列1個のサイズ

sizeof(配列[0])

第4引数 qsort比較関数(複数条件)

int 比較関数(const void *p1, const void *p2){
    //第1ソートキーを年齢とする
    int n1 = ((const 構造体枠_t *)p1)->年齢;
    int n2 = ((const 構造体枠_t *)p2)->年齢;
    if(n1 != n2){
        return n1 - n2;
    }
    //第2ソートキーを郵便番号とする
    const char *s1 = ((const 構造体枠_t *)p1)->郵便番号;
    const char *s2 = ((const 構造体枠_t *)p2)->郵便番号;
    return  strcmp(s1,s2);
}

第1ソートキーを年齢(若い順)
第2ソートキーを郵便番号(辞書並び)
としています。


●構造体編qsort使用例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
    char    名前[128];
    int     年齢;
    char    郵便番号[128];
} 構造体枠_t ;
static 構造体枠_t 配列[]={
    {.名前="佐藤",.年齢=98,   .郵便番号="111-2345"},
    {.名前="鈴木",.年齢=108, .郵便番号="222-3456"},
    {.名前="田中",.年齢=88,   .郵便番号="333-4567"},
    {.名前="加藤",.年齢=78,   .郵便番号="111-2345"},
    {.名前="伊藤",.年齢=78,   .郵便番号="333-4567"},
    {.名前="武藤",.年齢=78,   .郵便番号="222-3456"},
    {.名前="須藤",.年齢=78,   .郵便番号="111-2345"},
};
int 比較関数(const void *p1, const void *p2){
    //第1ソートキーを年齢とする
    int n1 = ((const 構造体枠_t *)p1)->年齢;
    int n2 = ((const 構造体枠_t *)p2)->年齢;
    if(n1 != n2){
        return n1 - n2;
    }
    //第2ソートキーを郵便番号とする
    const char *s1 = ((const 構造体枠_t *)p1)->郵便番号;
    const char *s2 = ((const 構造体枠_t *)p2)->郵便番号;
    return  strcmp(s1,s2);
}
void DMP(const char *mess,構造体枠_t ary[],size_t max){
    for(size_t i = 0;i < max;i++) {
        printf("%s:年齢=%-3d 〒=%s 名前=%s\n",
            mess,
            ary[i].年齢,
            ary[i].郵便番号,
            ary[i].名前
        );
    }
}
//  構造体ソート
int main(void){
    size_t  要素数 = sizeof(配列) / sizeof(配列[0]) ;
    
    DMP("ソート前△:",配列,要素数);
    
    qsort(配列,要素数,sizeof(配列[0]),比較関数);
    
    DMP("ソート後▼:",配列,要素数);
}

●構造体編実行結果

./a.out
ソート前△::年齢=98  〒=111-2345 名前=佐藤
ソート前△::年齢=108 〒=222-3456 名前=鈴木
ソート前△::年齢=88  〒=333-4567 名前=田中
ソート前△::年齢=78  〒=111-2345 名前=加藤
ソート前△::年齢=78  〒=333-4567 名前=伊藤
ソート前△::年齢=78  〒=222-3456 名前=武藤
ソート前△::年齢=78  〒=111-2345 名前=須藤
ソート後▼::年齢=78  〒=111-2345 名前=加藤
ソート後▼::年齢=78  〒=111-2345 名前=須藤
ソート後▼::年齢=78  〒=222-3456 名前=武藤
ソート後▼::年齢=78  〒=333-4567 名前=伊藤
ソート後▼::年齢=88  〒=333-4567 名前=田中
ソート後▼::年齢=98  〒=111-2345 名前=佐藤
ソート後▼::年齢=108 〒=222-3456 名前=鈴木