HOWTO · C

在 C 语言中实现字典

本文介绍了如何在 C 语言中实现字典。

本页内容

ISO C 没有内置的字典类型。如果目标环境提供 POSIX hsearch 接口,就可以使用其中的哈希表例程存储和检索键值条目。下面的示例会创建一张表、插入两个条目、查找一个已存在的键和一个不存在的键、检查返回的指针,并释放该表。这只是实现字典的一种方式,并不涵盖所有可能的设计。

使用 POSIX hsearch 例程在 C 中实现字典

hcreate、hsearch 和 hdestroy 是在 <search.h> 中声明的 POSIX 例程,不属于 ISO C 标准库。它们是否可用取决于目标操作系统和 C 库,因此在使用示例之前,请先确认你的环境提供此接口。hcreate 和 hsearch 的 POSIX 规范定义了该接口,可用于查阅相关要求。此处没有提供具体的平台或库版本信息。

使用 hcreate 和 hsearch 创建哈希表并添加条目

调用一次 hcreate,即可按指定容量创建哈希表。创建成功时,它返回非零值;无法创建表时返回零。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

输出块中的 compiler: not found 是保留原样的英文提示,意思是“未找到编译器”。

查找键并处理缺失条目

第一次 FIND 调用查找程序已插入且值为 C 的 language;第二次查找程序未插入的 compiler。程序在读取 data 前会检查每个结果,因此结果为 NULL 时会安全地进入“未找到”分支。如果本应存在的键查找结果为 NULL,示例也会避免解引用该指针,并报告查找结果。你可以根据应用需求调整提示消息或返回策略。

插入无法完成时,hsearch 也会返回 NULL,因此应在继续执行前检查每次 ENTER 调用的结果。示例通过向 stderr 写入诊断信息、释放已创建的表并返回失败状态,来处理创建和插入失败。应在每项操作后立即进行这些检查,确保后续代码只有在确认指针有效后才读取条目。

释放哈希表并了解其限制

不再需要哈希表时,调用 hdestroy。它会释放哈希表,但调用者仍需负责条目所引用的动态分配键或值存储;请保留这些指针,并单独释放自己拥有的内存。示例使用的是字符串字面量,因此没有这类内存需要释放。这里展示的 POSIX 接口不提供删除单个条目的操作。创建表时应选择足够的容量:请求的容量会传给 hcreate,而此处展示的接口流程没有提供调整表大小的操作。