Finding Structures with the bsearch() Function

It’s easy to explain the bsearch() function when using integers. One step up is to search for strings, covered in last week’s Lesson. At the pinnacle of insanity, however, is searching through an array of structures.

The bsearch() function locates a structure based on one of its members. The issues are setting the key as a structure (something I had to figure out), and coding the compare() function to properly manipulate the structures.

To test the function, I use this structure, potus:

struct potus {
    int index;
    char *name;
};

And this array of potus structures, named presidents[]:

struct potus presidents[] = {
    { 1, "Washington" }, { 2, "Adams" }, { 3, "Jefferson" },
    { 4, "Madison" }, { 5, "Monroe" }, { 6, "Adams" }
};

You can search this array for any member of the structure — but only one member. You cannot search for an entire structure as they must be sorted. In a database, sorting is based upon a specific key. For this example, I’m sorting based on the name string. Even so, the search key must be another structure; it cannot be a string by itself.

The following code shows how to search for a specific structure based on its name (string) member.

2025_02_01-Lesson.c

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

struct potus {
    int index;
    char *name;
};

int compare(const void *a, const void *b)
{
    char *name1 = ((struct potus *)a)->name;
    char *name2 = ((struct potus *)b)->name;
    return( strcmp(name1,name2) );
}

int main()
{
    struct potus presidents[] = {
        { 1, "Washington" }, { 2, "Adams" }, { 3, "Jefferson" },
        { 4, "Madison" }, { 5, "Monroe" }, { 6, "Adams" }
    };
    struct potus *r,*key;
    char input[32];
    int size;

    /* obtain array size */
    size = sizeof(presidents)/sizeof(presidents[0]);

    /* obtain input */
    printf("President: ");
    scanf("%s",input);
    /* the key must be a structure to match
       the index member need not be set */
    /* allocate storage for both the structure
       as well as the name */
    key = malloc( sizeof(struct potus) );
    key->name = malloc( sizeof(char) * 32 );
    if( key== NULL || key->name==NULL )
    {
        fprintf(stderr,"Unable to allocate structure\n");
        exit(1);
    }
    strcpy(key->name,input);

    /* sort the list */
    qsort(presidents,size,sizeof(struct potus),compare);

    /* locate a value */
    r = bsearch( key, presidents, size, sizeof(struct potus), compare );
    if( r!=NULL )
        printf("Key %s found!\n",r->name);
    else
        printf("Key %s not found.\n",key->name);

    return 0;
}

The array’s size is obtained by dividing the total array size, sizeof(presidents), by the size of a single member, sizeof(presidents[0]):

size = sizeof(presidents)/sizeof(presidents[0]);

The next step is to allocate the key structure, which is the same type of structure used in the array. Also — and this is important — the name member of the structure must also be allocated:

key = malloc( sizeof(struct potus) );
key->name = malloc( sizeof(char) * 32 );

(An issue might exist if the key allocation fails before trying key->name. In my example I test both together, though they should be tested separately to create more secure code.)

Once allocated, user input (stored in variable input) is copied into the key structure’s name member:

strcpy(key->name,input);

You need not assign a value to the index member as it’s unused, so whatever garbage in memory is okay.

Next the qsort() function sorts on the array based upon the name member. The compare() function uses two local pointers to obtain the name strings, then the strcmp() function is used in the return statement to cough up the results:

char *name1 = ((struct potus *)a)->name;
char *name2 = ((struct potus *)b)->name;
return( strcmp(name1,name2) );

Finally, bsearch() is called. But note that no ampersand prefixes the key argument as it’s already an address:

r = bsearch( key, presidents, size, sizeof(struct potus), compare );

Here’s a sample run:

President: Jefferson
Key Jefferson found!

As I wrote earlier, it took me a while to figure out that the first argument in the bsearch() function must be of the same data type (the potus structure) as the items in the array. I tried using a string again and again, but the program crashed merrily. Only when I looked at the compare() function did I realize that the data types must match for the code to run successfully.

Leave a Reply