0

I'm currently working in my beginner's programming course project and in it I have to perform several searches in arrays of structures, for example I have these two different structures:

typedef struct{
  char name[SIZE];
  int number, year, isbn;
}magazine;

typedef struct{
  char title[SIZE];
  char author[SIZE];
  int isbn, year;
}book;

As you can see, both book and magazine have isbn as a common element, and queries made with the isbn can be made for both data structures, my question is, how can I make (or use a glibc) function that is general purpose, without having to do this:

book *search_book(book *array, int key){
   //We search a book by its isbn and return the pointer.
}

magazine *search_mag(magazine *array, int key){
  //We search a magazine by its isbn and return the pointer
}

And instead be able to perform the search for both data structures in a single function?

3 Answers3

1

The other answers are great, but just because your code is in C, it doesn't mean it can't be in C++!

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

#define SIZE 256

struct Base {
    int isbn;
    char name[SIZE];
    int year;
};

struct Book {
    struct Base base; // Inheritance, C style
    int number;
};

struct Magazine {
    struct Base base;  // Inheritance, C style
    char author[SIZE];
};

/* 
You're going to need to cast the arguments and return value.
Also provide the size of the element.
However: the code is generic, casting always works (if `Base` is the first element) , and there is no need for IF or SWITCH statements.
*/
struct Base* search(struct Base* array,int length,size_t size,int key){
    for (int i=0;i<length;++i){
        if (array->isbn == key){
            return array;
        }
        // array[i] won't work, because the size of Base is not the size we need to jump in.
        array = (struct Base*)((uint8_t*)array + size);
    }
    return NULL;
}

int main(int argc,char** argv){

    // Initialize some data
    struct Book books[] = {
        { {123,"Moby Dick",1851},1 },
        { {124,"Oliver Twist",1837},2 }
    };
    struct Magazine magazines[] = {
        {{ 125,"Wired",2020 }, "Some author"},
        {{ 126,"Byte",1990 }, "Some author"}
    };

    // Search for a book and a magazine
    struct Book* book = (struct Book*)search((struct Base*)books,2,sizeof(struct Book),124);
    struct Magazine* magazine = (struct Magazine*)search((struct Base*)magazines,2,sizeof(struct Magazine),126);

    if (book){
        printf("Found book %s, number: %d\n",book->base.name,book->number);
    }
    if (magazine){
        printf("Found magazine %s, author: %s\n",magazine->base.name,magazine->author);
    }
}
Moshe Gottlieb
  • 3,963
  • 25
  • 41
  • Yeah, this is better. +1 – Petr Skocik May 28 '20 at 16:11
  • This is great, I'll see if I can implement it, because I also have to load my structure arrays from files, and I want to have only an active array at a time (using malloc to allocate memory and free() when I no longer need the array). – Luis Sebastian May 28 '20 at 16:23
0

You might consider using a struct like this and writing a function to check for the type and then either call one of the specialized functions that know how to search for books and magazines.

typedef enum { INVALID, BOOK, MAGAZINE } itemType;

typedef struct {
  union {
  book b;
  magazine m;
  } u;

  itemType type;
} Item;

You can initialize your array with your items, also setting the type for each one. The last item can be one with type INVALID to signify the end of the array, or you can pass a size to your search function.

The function might loosely look like this:

Item *search(Item *array, int key){
    Item * result = NULL;
    ...
    switch (array[i].type)
    {
       case BOOK: < do book search here using array[i].u.b >
                  if (found) result = array[i];
       break;
       case MAGAZINE: < do magazine search here using array[i].u.m>
                  if (found) result = array[i];
       break;
       default: // handle invalid case here
       break;
    }
    ...
    return result;
}
DNT
  • 2,356
  • 14
  • 16
0

This is where templates of C++ fit great. In pure C, however, this is a hell: First you would need a common type for your arrays, which is likely void*, but this also removes any type information.
Because of this, your search function then also needs to know the size of one element and the offset inside one element into the isbn member.

This is where pointer arithmetic comes into play and surely nothing you want to do as a beginner. If you want to go that way, have a look at sizeof and maybe the offsetof macro which is commonly used in C code, but I really think this is quite advanced.

mrksngl
  • 109
  • 4
  • I like to think that I'm familiar with pointer arithmetic, but it looks like that I'll have to do this the easy way, in the end this is just an optimization exercise, doing it in a way or another isn't luckily going to affect my grade. – Luis Sebastian May 28 '20 at 15:27