【6日でできるC言語入門】文字列の比較関数

C言語で配列やデータ構造をソートする際、文字列の並べ替えには「比較関数」が不可欠です。
 特に標準ライブラリの qsort 関数では、比較関数(コールバック)を自作して渡すことで、数値だけでなく文字列や構造体の任意のフィールド順に並び替えることができます。

 ここでは、文字列用の比較関数の書き方、strcmp 関数の正しい利用方法、qsort との組み合わせ、そして応用例まで詳しく解説します。

1.文字列の比較関数の基本

1.1. 文字列を比較する関数―strcmp

C言語の標準ライブラリには、文字列を辞書順で比較するための strcmp 関数が用意されています。

関数名書式戻り値
strcmpint strcmp(const char* s1, const char* s2);s1とs2の大小関係(下表参照)

strcmpの戻り値

戻り値意味
0s1とs2が完全一致
負の値s1がs2よりも辞書順で前にある。
正の値s1がs2よりも辞書順で後ろにある。

1.2. 比較関数として利用する理由

標準ソート関数qsortに「配列要素の比較ルール」を伝えるため、比較関数を自分で用意します。
特に文字列配列や構造体のメンバ(例:名前)でソートしたい場合、この関数が必要です。

2.文字列比較関数のサンプル実装

2.1. qsort用・文字列比較関数の典型例

int cmp_str(const void* a, const void* b) {
    // a, bはそれぞれchar*型(配列要素)のアドレス(=char**型)
    return strcmp(*(const char**)a, *(const char**)b);
}

なぜconst void*なのか?

  • qsortの仕様で、比較関数の引数は const void* 型。
  • 文字列配列(char* array[])の場合、qsortから受け取るa, bは「要素(char*)のアドレス」→型はconst char**
  • したがって、*(const char**)aで実際の文字列(char*)を取り出す。

図解

実際の要素配列の型qsortから渡る型比較関数内の処理
"apple"char*const void*(const char*)a → char*
"lemon"char*const void*(const char*)b → char*

2.2. サンプルプログラム:文字列配列のソート

プロジェクト/ファイル名: Lesson72_1/main.c

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

#define SIZE 5

int cmp_str(const void* a, const void* b) {
    return strcmp(*(const char**)a, *(const char**)b);
}

void show(char* array[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%s ", array[i]);
    }
    printf("\n");
}

int main(void) {
    char* fruits[SIZE] = {"みかん", "りんご", "バナナ", "ぶどう", "すいか"};
    printf("初期配列: ");
    show(fruits, SIZE);

    qsort(fruits, SIZE, sizeof(char*), cmp_str);

    printf("ソート後: ");
    show(fruits, SIZE);
    return 0;
}

実行結果

初期配列: みかん りんご バナナ ぶどう すいか
ソート後: すいか ぶどう みかん りんご バナナ

※ 文字コードによる並び順になるため、結果はロケール・環境により異なる場合があります

3.応用:構造体配列の比較関数

3.1. 構造体の特定メンバ(例:名前)でソート

構造体配列をqsortでソートしたい場合、比較関数内で必要なフィールドをstrcmpで比較します。

プロジェクト/ファイル名: Lesson72_2/main.c

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

typedef struct {
    int id;
    char name[16];
} Student;

int cmp_by_name(const void* a, const void* b) {
    // a, bはStudent型のアドレス
    const Student* sa = (const Student*)a;
    const Student* sb = (const Student*)b;
    return strcmp(sa->name, sb->name);
}

void show(Student* arr, int size) {
    for (int i = 0; i < size; i++) {
        printf("ID:%d 名前:%s\n", arr[i].id, arr[i].name);
    }
}

int main(void) {
    Student students[] = {
        {3, "佐藤"}, {1, "田中"}, {2, "山田"}, {5, "加藤"}, {4, "鈴木"}
    };
    int size = sizeof(students) / sizeof(students[0]);

    printf("ソート前:\n");
    show(students, size);

    qsort(students, size, sizeof(Student), cmp_by_name);

    printf("ソート後:\n");
    show(students, size);
    return 0;
}

実行結果

ソート前:
ID:3 名前:佐藤
ID:1 名前:田中
ID:2 名前:山田
ID:5 名前:加藤
ID:4 名前:鈴木
ソート後:
ID:5 名前:加藤
ID:3 名前:佐藤
ID:2 名前:山田
ID:1 名前:田中
ID:4 名前:鈴木

まとめ

  • 文字列の比較関数は、strcmpを使ってqsortの比較関数として実装する。
  • 配列の型や目的に合わせて、ポインタのキャストやアクセス方法に注意する。
  • 応用すれば、構造体配列の特定メンバでのソートや、独自の並び替え基準も柔軟に実装できる。