cbsearch

Search for a target string using bsearch in an array of string in a struct


Hi I'm using bsearch to search for a target string in an array of string in a struct and return the index of the string. However, so far it is always returning NULL, and I'm pretty sure the way I'm getting the array index is also not right:

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

#define MAX_ITEM_SIZE 10
#define MAX_NAME_LEN 20

typedef struct fruit_shelf_s {
    char fruit_name[MAX_NAME_LEN + 1]; // +1 for null
    int max_shelf_size;
    int current_shelf_occupied;
} fruit_shelf_t;

typedef struct food_aisle_s {
    fruit_shelf_t fruits[MAX_ITEM_SIZE];
} food_aisle_t;

void load_fruit_shelf(food_aisle_t *food_aisle, char sample[MAX_ITEM_SIZE][MAX_NAME_LEN]) {
    for(int i; i < MAX_ITEM_SIZE; i++){
        // buffer siE large enough so it does not over flow
        strncpy(food_aisle->fruits[i].fruit_name, sample[i], strlen(sample[i])+1);
    }
    for (int j = 0; j < MAX_ITEM_SIZE; j++) {
        printf("%s\n", food_aisle->fruits[j].fruit_name);
    }   
}

int myStrCmp(const void *s1, const void *s2) {
    printf("myStrCmp: s1(%p): %s, s2(%p): %s\n", s1, *(char **)s1, s2, *(char**)s2);
    return strcmp(*(char **) s1, *(char **) s2);
}

int binary_search(food_aisle_t* food_aisle, char *key){
    char *buff = (char*)bsearch(&key, food_aisle->fruits, MAX_ITEM_SIZE, sizeof(food_aisle_t), (int(*)(const void*,const void*)) strcmp);
    if(!buff){
        int index = (buff - food_aisle->fruits[0].fruit_name) / sizeof(food_aisle->fruits[0]);
        printf("Found key = %s at index = %d.\n", buff, index);
        return 0;
    } else {
        printf("Did not find key = %s\n", key);
        return -1;
    }
}

char sample[MAX_ITEM_SIZE][MAX_NAME_LEN] = {"Apple", "Banana", "Orange", "Grape", "Strawberry", "Pineapple", "Jackfruit", "Lemon", "Lime", "Watermelon"};

int main () {
    food_aisle_t food_aisle;
    // Empty the array;
    for (int i = 0; i < MAX_ITEM_SIZE; i++) {
        memset(food_aisle.fruits[0].fruit_name, 0, sizeof(food_aisle.fruits[i].fruit_name));
    }

    // load the array
    load_fruit_shelf(&food_aisle, sample);

    char *key = "Orange";
    int num = binary_search(&food_aisle, key);
    if(num == 0){
        printf("key found");
    } else {
        printf("key not found");
    }

    return(0);
}

So I'm not sure if the reason I'm getting NULL is because the string comparison function. So I wrote myStrCmp to see if I can see the address of the array. But I got an Segfault:

Apple
Banana
Orange
Grape
Strawberry
Pineapple
Jackfruit
Lemon
Lime
Watermelon
Segmentation fault (core dumped)

I experimented with the arguments but I still get NULL:

// change to double pointer to reference to the array of string
char **buff = (char**)bsearch(&key, food_aisle->fruits, MAX_ITEM_SIZE, sizeof(food_aisle_t*), (int(*)(const void*,const void*)) strcmp);

// change the size type the searching element to sizeof(fruit_shelf_t*)
char *buff = (char*)bsearch(&key, food_aisle->fruits, MAX_ITEM_SIZE, sizeof(fruit_shelf_t*), (int(*)(const void*,const void*)) strcmp);

// change the size type the searching element to sizeof(char*)
char *buff = (char*)bsearch(&key, food_aisle->fruits, MAX_ITEM_SIZE, sizeof(char*), (int(*)(const void*,const void*)) strcmp);

As for getting the index of the string, I think the following idea should work, but I can't verify it since I can't get the correct search result. And sometimes I got negative values which is defiantly wrong:

index = (result_element_add - start_add_of_the_array) / size_of_the_objects_in_the_array

I tried the following alternatives:

// Try 1:
int index = (buff - food_aisle->fruits[0].fruit_name) / sizeof(food_aisle->fruits[MAX_ITEM_SIZE]);

returned:

Found key = (null) at index = 333807782.
key found
// Try 2:
int index = (buff - food_aisle->fruits[0].fruit_name) / sizeof(food_aisle->fruits[MAX_ITEM_SIZE].fruit_name);

returned:

Found key = (null) at index = -319396745.
key found
// Try 3:
int index = (buff - food_aisle->fruits[0].fruit_name) / sizeof(food_aisle->fruits[0]);

returned:

Found key = (null) at index = 233548559.
key found

What might be the issue for the bsearch() usage here?


Solution

    1. You are passing strcmp to bsearch. strcmp takes const char* arguments, bsearch epects a function with const void*.
    2. You are passing strcmp to bsearch. It compares strings, not elements of fruit_shelf_t.
    3. myStrCmp still compares strings, not fruit_shelf_t with key.
    4. Do not cast the result of bsearch. Do not cast the function pointer to a different type.
    5. for(int i; i < is not initializting i...
    6. if(!buff){ checks if buff is not set. Should be if(buff){.

    1. I see no reason to pass a pointer to a char *, just pass the key itself.
    2. The callback from bsearch compares key with the element from the array.

    int myCompareFruits(const void *s1, const void *s2) {
        const char *key = s1;
        const fruit_shelf_t *b = s2;
        printf("%s %s\n", key, b->fruit_name);
        return strcmp(key, b->fruit_name);
    }
    
    int binary_search(food_aisle_t* food_aisle, char *key){
        fruit_shelf_t *buff = bsearch(key, food_aisle->fruits,
            MAX_ITEM_SIZE, sizeof(fruit_shelf_t), myCompareFruits);
        if (buff) {