HOWTO · C

C 言語で辞書の実装

この記事では、C 言語で辞書を実装する方法を紹介します。

このページの内容

ISO C には、組み込みの辞書型は定義されていません。対象環境が POSIX の hsearch インターフェースを提供している場合、そのハッシュテーブル用ルーチンを使ってキーと値の組を保存・取得できます。以下の例では、テーブルを作成し、2つの項目を登録してから、存在するキーと存在しないキーを検索します。戻り値のポインターを確認した後、テーブルを解放します。ここで示すのは辞書の設計方法の一例です。

POSIX の hsearch ルーチンで C の辞書を実装する

hcreate、hsearch、hdestroy は <search.h> で宣言される POSIX ルーチンであり、ISO C ライブラリの関数ではありません。利用できるかどうかは対象の OS と C ライブラリによって異なるため、例を使う前に環境がこのインターフェースを提供していることを確認してください。hcreate と hsearch の POSIX 仕様で、インターフェースや要件を確認できます。この例が対象とする特定のプラットフォームやライブラリのバージョンは示されていません。

hcreate と hsearch でハッシュテーブルを作成し項目を追加する

hcreate を一度呼び出し、指定した容量のテーブルを作成します。成功すると 0 以外を返し、作成できない場合は 0 を返します。ENTRY は char * 型のキーと void * 型のデータポインターを保持します。hsearch には ENTRY と ACTION の値を渡します。ENTER は項目の登録、FIND は検索を要求します。成功時は項目へのポインターが返り、失敗時は NULL が返ります。

次の例を実行します。

このセクションのコードと表示されるラベルや出力は英語のままです。

#define _XOPEN_SOURCE 700
#include <search.h>
#include <stdio.h>

int main(void) {
    if (hcreate(4) == 0) {
        fputs("could not create table\n", stderr);
        return 1;
    }

    ENTRY item = {"language", "C"};
    if (hsearch(item, ENTER) == NULL) {
        fputs("could not insert language\n", stderr);
        hdestroy();
        return 1;
    }

    item.key = "standard";
    item.data = "POSIX";
    if (hsearch(item, ENTER) == NULL) {
        fputs("could not insert standard\n", stderr);
        hdestroy();
        return 1;
    }

    ENTRY query = {"language", NULL};
    ENTRY *found = hsearch(query, FIND);
    if (found == NULL) {
        puts("language: not found");
    } else {
        printf("language: %s\n", (char *)found->data);
    }

    query.key = "compiler";
    found = hsearch(query, FIND);
    if (found == NULL) {
        puts("compiler: not found");
    } else {
        printf("compiler: %s\n", (char *)found->data);
    }

    hdestroy();
    return 0;
}
language: C
compiler: not found

キーを検索し、見つからない場合に対処する

最初の FIND 呼び出しでは、プログラムが値 C とともに登録した language を検索します。2回目は登録していない compiler を検索します。どちらの場合も、プログラムは data を読む前に結果を確認するため、戻り値が NULL でも安全に「見つからない」場合を処理できます。本来存在するはずのキーの検索で NULL が返った場合も、例ではポインターを参照せずに結果を報告します。メッセージや戻り値の扱いは、用途に合わせて変更してください。

挿入を完了できない場合にも hsearch は NULL を返すため、処理を続ける前に各 ENTER の結果を確認します。例ではテーブルの作成や項目の登録に失敗したとき、stderr に診断メッセージを書き込み、すでに作成済みのテーブルを解放して、失敗を示すステータスを返します。各操作の直後に確認すれば、後続のコードでポインターを確かめてから項目を読むことができます。

変更されない出力例に表示される「compiler: not found」は英語のラベルです。日本語訳ではなく、例の中でも英語のままです。

テーブルを解放し、制限を把握する

テーブルを使い終えたら hdestroy を呼び出します。これはテーブルを解放しますが、項目が参照する動的に確保されたキーや値の領域は呼び出し側が管理します。それらのポインターを保持し、所有する領域は別途解放してください。この例では文字列リテラルを使っているため、そのような領域の解放は必要ありません。ここで示す POSIX インターフェースには、個々の項目を削除する操作がありません。テーブルを作成するときは十分な容量を選んでください。指定した容量は hcreate に渡しますが、この例で扱うインターフェースにはサイズ変更の操作もありません。