4

I have a struct that looks like this:

typedef struct dictionary_t{
    char word[30];
    int foo;
    int bar;
} dictionary_t;

Which forms an ordered array:

dictionary_t dictionary[100];

I would like to search this array for a string using bsearch() and get a pointer to the struct. So far this has worked:

dictionary_t* result;
char target[30] = "target";
result = bsearch(&target, dictionary, dict_length, sizeof(dictionary_t), (int(*)(const void*,const void*)) strcmp);

However this is a bit of a hack and only works because the string happens to be the first member of the struct. What would be a better way to find a string within an array of structs and return a pointer to the struct?

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
Myrmidon
  • 65
  • 1
  • 5
  • 1
    You need to implement a compare function which will be aware of the `dictionary_t` type structure and pass it instead of `strcmp`. It's pretty easy in your case - just a wrapper around `strcmp`. – Eugene Sh. May 09 '17 at 14:34
  • An example of a custom comparison function is at the bottom of the [standards page](http://pubs.opengroup.org/onlinepubs/009696799/functions/bsearch.html) for `bsearch()`. – cxw May 09 '17 at 14:35
  • this is actually wrong, you need to write your own comparison function with the correct signature, then assign the const void pointers to const bointers of your structure and call strncmp() on the members –  May 09 '17 at 14:37
  • Thanks for the quick replies :). I'll read up a bit on writing comparison functions and give it a shot. – Myrmidon May 09 '17 at 14:42

1 Answers1

3

You should implement your own comparator function and pass it in. The most important (non-trivial) thing to keep in mind here is that according to the standard,

The implementation shall ensure that the first argument is always a pointer to the key.

This means that you can write a comparator that compares a string such as target and a dictionary_t object. Here is a simple function that compares your stucts to a string:

int compare_string_to_dict(const void *s, const void *d) {
    return strncmp(s, ((const dictionary_t *)d)->word, sizeof(((dictionary_t *)0)->word));
}

You would then pass it by name as a normal function pointer to bsearch:

result = bsearch(target, dictionary, dict_length, sizeof(dictionary_t), compare_string_to_dict);

Note that target does not need to have its address passed in since it is no longer mocking a struct.

In case you are wondering, sizeof(((dictionary_t *)0)->word) is an idiomatic way of getting the size of word in dictionary_t. You could also do sizeof(dictionary[0].word) or define a constant equal to 30. It comes from here.

Community
  • 1
  • 1
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264