Home > Mobile >  How to model a book-library system in C using structs?
How to model a book-library system in C using structs?

Time:07-13

I am trying to learn C and C and I am struggling a bit with pointers. Concretely, I want to model a book-library system using some structs. A library could have more or less books, but we do not exactly the number. Below, I will post some snippet code:

This works but the number of books is known, not desired.

// library.h
#ifndef _LIBRARY_
#define _LIBRARY_

#include "book.h"

typedef struct {
    int noBooks;
    book books[10];
}library;

void addBook(book*, library*);

#endif 

// library.c
#include "library.h"

void addBook(book* b, library* l) {
    l->books[l->noBooks] = *b;
    l->noBooks  ;
}

// book.h
#ifndef _BOOK_
#define _BOOK_

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

    typedef struct book_ {
        int id;
        char author[15];
        char title[15];
    }book;
    
    void displayInfo(const book*);
    
    #endif

//book.c
#include "book.h"

void displayInfo(const book* b) {
    printf("Id: %d \n Author: %s \n Title: %s\n", b->id, b->author, b->title);
}

//main.cpp
#include<iostream>

extern "C" {
    #include "book.h"
    #include "library.h"
}

int main() {
    std::cout << "Start" << std::endl;

    book b1;
    strcpy_s(b1.author, "Ab");
    b1.id = 1;
    strcpy_s(b1.title, "Ab");

    book b2;
    strcpy_s(b2.author, "Df");
    b1.id = 2;
    strcpy_s(b2.title, "Df");

    library l1;
    l1.noBooks = 0;
    addBook(&b1, &l1);
    addBook(&b2, &l1);

    std::cout << l1.books[0].author << "\n";
    std::cout << l1.books[1].author;
}

How should I modify it in order to add books without to know exactly the number (10 in this case)? I tried more cases, for example:

// library.h
#ifndef _LIBRARY_
#define _LIBRARY_

#include "book.h"

typedef struct {
    int noBooks;
    book* books;
}library;

void addBook(book*, library*);

#endif 

// library.c
#include "library.h"

void addBook(book* b, library* l) {
    l->books[l->noBooks] = *b;
    l->noBooks  ;
}

Still, it does not work. Could you give me guidance/advice? Thank you for your time!

CodePudding user response:

If you want to do this in C:

You can try to implement your own resizable library.

Something like this :

typedef struct {
    int length;
    int capacity;
    book *books;
} library;

int add_book(library *lib, book *b) {
    // Add a book to the library
    assert(lib != NULL); // Check if library is valid
    assert(b != NULL); // Check if book is valid

    if(lib->length   1 > lib->capacity) { // We run out of space for new books
        lib->capacity *= 2;
        lib->books = realloc(lib->books, lib->capacity * sizeof(book)); // Check for errors
        lib->books[lib->length] = *b;
        lib->length  ;
        printf("Increased library capacity and added book to library\n");
        return 0;
    }
    else {
        lib->books[lib->length] = *b;
        lib->length  ;
        printf("Book added to library\n");
        return 0;
    }
}

If you want to do this in C (much easier): You can use vector and just append values.

CodePudding user response:

Here is a minimal implementation demonstrating how to dynamically add a book to your library and clean up the resources allocated:

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

struct book {
    int id;
    char author[15];
    char title[15];
};

struct library {
    unsigned noBooks;
    struct book *books;
};

// check for duplicate id?
struct library *library_add(struct library *l, struct book *b) {
    if(!l) return NULL;
    struct book *books = realloc(l->books, sizeof(struct book) * (l->noBooks   1));
    if(!books) {
        printf("realloc failed\n");
        return NULL;
    }
    l->books = books;
    l->books[l->noBooks].id = b->id;
    strcpy(l->books[l->noBooks].author, b->author);
    strcpy(l->books[l->noBooks].title, b->title);
    l->noBooks  ;
    return l;
}

void library_print(struct library *l) {
    if(!l) return;
    for(unsigned i = 0; i < l->noBooks; i  ) {
        printf("id = %d, author = %s, title = %s\n",
            l->books[i].id,
            l->books[i].author,
            l->books[i].title
        );
    }
}

void library_destroy(struct library *l) {
    if(!l) return;
    free(l->books);
}

int main() {
    struct library l = { 0, NULL };
    library_add(&l, &(struct book) { 1, "Ab", "Ab"});
    library_add(&l, &(struct book) { 2, "Df", "Df"});
    library_print(&l);
    library_destroy(&l);
    return 0;
}

CodePudding user response:

Given the discussion in the comments about what your code is, I'll chip in an answer that leans more into C .

#include <iostream>
#include <string>
#include <vector>

class Book {
 public:
  Book() = default;
  Book(int id, std::string auth, std::string title)
      : m_id(id), m_author(auth), m_title(title) {}

  friend std::ostream& operator<<(std::ostream& sout, const Book& book) {
    return sout << "ID: " << book.m_id << "\nAuthor: " << book.m_author
                << "\nTitle: " << book.m_title << '\n';
  }

 private:
  int m_id = 0;
  std::string m_author;
  std::string m_title;
};

class Library {
 public:
  void add_book(Book book) { m_books.push_back(book); }
  std::size_t size() const { return m_books.size(); }

  // The better approach would be to provide iterators so that you can access
  // individial books from the library, much like you can with the vector,
  // but that's a whole other level of work.
  void print() const {
    for (auto book : m_books) {
      std::cout << book << '\n';
    }
  }

 private:
  std::vector<Book> m_books;
};

int main() {
  Book b1(1, "Frank Herbert", "Dune");
  Book b2(2, "Frank Herbert", "Dune Messiah");

  Library l1;
  l1.add_book(b1);
  l1.add_book(b2);

  l1.print();
  std::cout << "Number of books in library: " << l1.size() << '\n';
}

Output:

ID: 1
Author: Frank Herbert
Title: Dune

ID: 2
Author: Frank Herbert
Title: Dune Messiah

Number of books in library: 2

As you can see, even a basic C (not C with std::cout, but idiomatic C ) implementation requires essentially a full re-write. And I noted one area where I really skimped. Your Library is just wrapping a container, and containers are typically built to be iterated through. But the effort of writing an iterator (it would just be wrapping the vector iterator) just didn't make sense for this answer.

For a toy program like this, you might be better off skipping the Library class completely and just declaring something like std::vector<Book> library; in your main() function and taking direct advantage of all the capabilities that std::vector provides.

The big takeaways in this snippet that will make the code easier to manage are:

  • Classes. Bundles your relevant data and functions together.
  • std::vector. A heap-allocated array that grows when needed and knows its own size.
  • std::string. Handles all the messy parts and far less error-prone.

Also on display is operator overloading. This allows you use your custom types as if they were native types. Much nicer than something like a display() function.

While all that C syntax is technically valid C , philosophically it's completely wrong. You either want to learn C, or you want to learn C . Choose one and stick with it. Blending the two the way you did will just lead to a bad time.

  • Related