hsearch(3)

๐Ÿ’– hsearch(3) โ€” hash table management ๐Ÿ’–

okay bestie, listen. this is hsearch(3), a page from the Library Functions Manual, and it's literally all about keeping your stuff in a hash table so you can find it again like it's nothing. no cap. let's get into it ๐Ÿซถ


โœจ NAME โœจ

hcreate, hdestroy, hsearch, hcreate_r, hdestroy_r, hsearch_r โ€” hash table management. that's the whole vibe. ๐ŸŒธ

๐Ÿ“š LIBRARY ๐Ÿ“š

Standard C library โ€” that's libc, link it with -lc. iconic behavior.

๐Ÿงš SYNOPSIS ๐Ÿงš

#include <search.h>

int hcreate(size_t nel);
void hdestroy(void);

ENTRY *hsearch(ENTRY item, ACTION action);

#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <search.h>

int hcreate_r(size_t nel, struct hsearch_data *htab);
void hdestroy_r(struct hsearch_data *htab);

int hsearch_r(ENTRY item, ACTION action, ENTRY **retval,
              struct hsearch_data *htab);

note the #define _GNU_SOURCE before the second #include โ€” that's the price of admission for the _r crew, and it points you at feature_test_macros(7). ๐Ÿ’…

๐ŸŒˆ DESCRIPTION ๐ŸŒˆ

so the trio hcreate(), hsearch(), and hdestroy() let you create and manage a hash search table full of entries, where each entry is a key (a string) plus some associated data. sounds amazing right? โš ๏ธ the catch: with these three, only one hash table can be in use at a time. one. single. table. monogamy table. ๐Ÿ’€

then we have the glow-up versions โ€” hcreate_r(), hsearch_r(), hdestroy_r() โ€” the reentrant ones, so your program can run multiple hash search tables at the same time. main character energy for real. the last argument, htab, points to a structure describing which table the function should operate on. and bestie, hear me: the programmer is supposed to treat that structure as opaque โ€” like, do not try to directly access or poke at the fields in there. hands off. ๐Ÿšซ

  • first things first โ€” you gotta create a hash table with hcreate(). the argument nel is the maximum number of entries in the table. and no, this maximum cannot be changed later, so choose it wisely ๐Ÿ˜ญ. the implementation is allowed to bump this value upward to make the resulting hash table perform better.
  • hcreate_r() does the exact same job as hcreate() but for the table described by the structure *htab. and yes: the structure that htab points at must be zeroed before the first call to hcreate_r(). zero it first, bestie, i'm begging.
  • hdestroy() frees the memory taken up by the hash table that hcreate() made. once you've called hdestroy(), you can create a new hash table with hcreate() again. โœจ
  • hdestroy_r() does the analogous thing for a hash table described by *htab, which was previously created using hcreate_r().

now, the search:

hsearch() searches the hash table for an item with the same key as item โ€” and "the same" here is decided using strcmp(3) โ€” and if it works out, it returns a pointer to it. slay.

the argument item is of type ENTRY, which is defined in <search.h> like this:

typedef struct entry {
    char *key;
    void *data;
} ENTRY;
  • the field key points to a null-terminated string, and that's your search key.
  • the field data points to the data associated with that key.

the argument action decides what hsearch() does after a search comes up empty. it has to be one of these two:

  • ENTER โ€” insert a copy of item, and return a pointer to the new hash table entry as the function result.
  • FIND โ€” return NULL.

(and if action is FIND, then data gets ignored, periodt.)

hsearch_r() is like hsearch() but it operates on the hash table described by *htab. the difference? instead of handing you the pointer as the function result, it puts a pointer to the found item into *retval. โœจ

๐ŸŽ€ RETURN VALUE ๐ŸŽ€

  • hcreate() and hcreate_r() return nonzero on success. they return 0 on error, and set errno to say which error it was.
  • on success, hsearch() returns a pointer to an entry in the hash table.
  • hsearch() returns NULL on error โ€” that means either action was ENTER and the hash table is full, or action was FIND and item couldn't be found in the hash table.
  • hsearch_r() returns nonzero on success and 0 on error.
  • when one of those two errors happens, they set errno to indicate the error. ๐Ÿ“

๐Ÿ’€ ERRORS ๐Ÿ’€

hcreate_r() and hdestroy_r() can fail for this reason:

  • EINVAL โ€” htab is NULL.

hsearch() and hsearch_r() can fail for these reasons:

  • ENOMEM โ€” action was ENTER, key was not found in the table, and there was no room in the table to add a new entry. ๐Ÿ˜ญ
  • ESRCH โ€” action was FIND, and key was not found in the table.
heads up: POSIX.1 specifies only the ENOMEM error. the rest are a bonus. ๐Ÿ“

๐Ÿฆ‹ ATTRIBUTES ๐Ÿฆ‹

for an explanation of the terms used in this section, go see attributes(7). (it's giving "read the fine print" but make it cute.)

  • hcreate(), hsearch(), hdestroy() โ€” Thread safety: MT-Unsafe race:hsearch
  • hcreate_r(), hsearch_r(), hdestroy_r() โ€” Thread safety: MT-Safe race:htab

๐Ÿ‘‘ STANDARDS ๐Ÿ‘‘

  • hcreate(), hsearch(), hdestroy() โ€” POSIX.1-2008.
  • hcreate_r(), hsearch_r(), hdestroy_r() โ€” GNU.

๐Ÿ“œ HISTORY ๐Ÿ“œ

  • hcreate(), hsearch(), hdestroy() โ€” SVr4, POSIX.1-2001.
  • hcreate_r(), hsearch_r(), hdestroy_r() โ€” GNU.

๐Ÿ”ฅ NOTES ๐Ÿ”ฅ

hash table implementations are usually more efficient when the table has enough free space to keep collisions to a minimum. typically that means nel should be at least 25% larger than the maximum number of elements you actually expect to store in the table. do the math, bestie. ๐Ÿงฎ

and now the part that gets everybody: hdestroy() and hdestroy_r() do not free the buffers pointed to by the key and data elements of the hash table entries. why? because it literally can't โ€” it has no idea whether those buffers were allocated dynamically. so if those buffers need freeing (say, because your program keeps creating and destroying hash tables instead of making one single table whose lifetime matches the whole program), then your program has to keep its own bookkeeping data structures so it can free them itself. the manual is not gonna do your homework for you. ๐Ÿ™ˆ

๐Ÿ› BUGS ๐Ÿ›

okay so here's the tea: SVr4 and POSIX.1-2001 specify that action is only significant for unsuccessful searches, which means an ENTER shouldn't do anything at all for a successful search. but in libc and glibc (before glibc 2.3), the implementation violates the specification and updates the data for the given key in that case. ๐Ÿ’€ not it.

also: individual hash table entries can be added, but not deleted. that's just the rules. ๐ŸŽ€

๐ŸŒธ EXAMPLES ๐ŸŒธ

this program inserts 24 items into a hash table and then prints some of them. iconic.

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

static char *data[] = { "alpha", "bravo", "charlie", "delta",
     "echo", "foxtrot", "golf", "hotel", "india", "juliet",
     "kilo", "lima", "mike", "november", "oscar", "papa",
     "quebec", "romeo", "sierra", "tango", "uniform",
     "victor", "whisky", "x-ray", "yankee", "zulu"
};

int
main(void)
{
    ENTRY e;
    ENTRY *ep;

    hcreate(30);

    for (size_t i = 0; i < 24; i++) {
        e.key = data[i];
        /* data is just an integer, instead of a
           pointer to something */
        e.data = (void *) i;
        ep = hsearch(e, ENTER);
        /* there should be no failures */
        if (ep == NULL) {
            fprintf(stderr, "entry failed\n");
            exit(EXIT_FAILURE);
        }
    }

    for (size_t i = 22; i < 26; i++) {
        /* print two entries from the table, and
           show that two are not in the table */
        e.key = data[i];
        ep = hsearch(e, FIND);
        printf("%9.9s -> %9.9s:%d\n", e.key,
               ep ? ep->key : "NULL", ep ? (int) ep->data : 0);
    }
    hdestroy();
    exit(EXIT_SUCCESS);
}

๐Ÿ’œ SEE ALSO ๐Ÿ’œ

bsearch(3), lsearch(3), malloc(3), tsearch(3) โ€” go read those next, they're the squad. ๐Ÿซถ


and that's the whole page, bestie!! this was Linux man-pages 6.20, dated 2026-02-08, and the page is hsearch(3). go forth and hash responsibly ๐Ÿ’…โœจ

๐Ÿ“ฆ where this page comes from
  • arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • ๐Ÿ“œ the page's license: GPL-2.0-or-later
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms