0

I have an array of structs that has already been sorted using qsort. I'm trying to search for a name in struct however it is always returning NULL. What is the explanation for why this is happening?

Here is my code:

#include "card.h"
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int cmpname (const void *pa, const void *pb) {
    const char *p1 = pa;
    const index_t * const *p2 = pb;
    //printf("Comparing: %s %s\n", p1, (char*)*p2);
        return strcmp((p1), (*p2)->name);
}

int main(int argc, char **argv) 
{
    unsigned length;
    int i = 0;
    int length_multiplier = 0;
    char *input = NULL;
    char *pItem;
    index_t **indexs = NULL;
    FILE *input_file;
    
    if((input_file = fopen("index.bin", "rb")) != NULL)
    {
        
        // GETS USER INPUT
        printf(">> ");
        size_t num_read = 0;

        getline(&input, &num_read, stdin); 
        printf("Searching for: %s\n", input);
        if(*input != 113)
        {
            // READING FILE AND ADDING ENTRIES INTO STRUCT
            //int *key;
            //int *pItem;
            //input_file = fopen("index.bin", "rb");
            if (input_file == NULL)
            {
                printf("ERROR OPENING FILE\n");
                free(indexs);
                fclose(input_file);
                return 1;
            }
            // READS THE INPUT FILE
            for(i = 0; fread(&length, sizeof(unsigned int), 1, input_file) != 0; i++)
            {
                length_multiplier++;
                indexs = realloc(indexs, sizeof(index_t *) * length_multiplier);
                indexs[i] = malloc(sizeof(index_t));
        
                indexs[i]->name = malloc(sizeof(char) * length + 1);
                fread(indexs[i]->name, sizeof(char), length, input_file);
                indexs[i]->name[length] = 0;
                fread(&indexs[i]->offset, sizeof(long), 1, input_file);
            }
            for(i = 0; i < length_multiplier; i++)
            {
                printf("%s\n", indexs[i]->name);
            }
            printf("Searching for: %s\n", input);
            
            pItem = (char*) bsearch(&input, indexs, length_multiplier, sizeof(indexs), cmpname);
    
            if(pItem == NULL)
            {
                printf("%s not found\n", pItem);
            }
            else
            {
                printf("found %s\n", pItem);
            }
    
            //FREEING MEMORY
            for(int i = 0; i < length_multiplier; i++)
            {
                free(indexs[i]->name);
                free(indexs[i]);
            }
            free(indexs);
            free(input);
    
            //fread(card[0]->id, sizeof(u_int32_t), 1, input_file);
            fclose(input_file);
    
            return 0;
        }
        else
        {
            printf("Exiting...\n");
            return 1;
        }
    }
    else
    {
        fprintf(stderr, "./parser: cannot open(%s%s%s): No such file or directory", "\"", argv[1], "\"");
        return 1;
    }
        
}

Here is my declared struct:

typedef struct index {
    char *name;
    long offset;
} index_t;
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
csDreamer
  • 11
  • 3

2 Answers2

1

The comparator function should compare two of whatever is in the array, not two different things. In your case, you want to compare two index_t *, not an index_t * and a char *. So it might look like this:

int cmpname (const void *pa, const void *pb) {
    const index_t * const *p1 = pa;
    const index_t * const *p2 = pb;
    //printf("Comparing: %s %s\n", p1, (char*)*p2);
        return strcmp((*p1)->name, (*p2)->name);
}
Sam Henke
  • 369
  • 2
  • 8
1

The variable indexs is declared like

index_t **indexs = NULL;

and points to the first element of an array with the element type index_t *.

indexs = realloc(indexs, sizeof(index_t *) * length_multiplier);

So in the call of bsearch

pItem = (char*) bsearch(&input, indexs, length_multiplier, sizeof(indexs), cmpname);

the object that accepts the result shall have the type index_t ** because the function returns a pointer to the target element.

So the variable pItem shall be declared like

index_t **pItem;

And as the element of the array has the type index_t * then you have to use the argument sizeof( index_t * ) instead of sizeof( indexs ).

On the other hand, the first parameter also shall be a pointer to an object of the type index_t *.

So before calling the function you could write for example

index_t input_item = { .name = input, .offset = 0 };
index_t *p_input_item = &input_item;

And the call of bsearch will look like

pItem = bsearch( &p_input_item, indexs, length_multiplier, sizeof( index_t * ), cmpname );

And then you could write

        if(pItem == NULL)
        {
            printf("%s not found\n", input);
        }
        else
        {
            printf("found %s\n", input);
        }

At last the comparison function can look like

int cmpname (const void *pa, const void *pb) {
    const index_t * const *p1 = pa;
    const index_t * const *p2 = pb;

    return strcmp( ( *p1 )->name, (*p2)->name );
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Re: *"The first parameter also shall be a pointer to an object of the type index_t *"* ---> Is that required by the standard or is it because OP's comparison function was written as such? – Harith Feb 10 '23 at 08:40
  • 1
    @Haris From the description of bsearch "2 The bsearch function searches an array of nmemb objects, the initial element of which is pointed to by base, for an element that matches the object pointed to by key. The size of each element of the array is specified by size." – Vlad from Moscow Feb 10 '23 at 08:50