You might declare a global or static variable, assuming BOOK_NUM
is some #define
-d constant (e.g. #define BOOK_NUM 100
somewhere before in your code):
book library[BOOK_NUM];
However, heap allocation is generally preferable, because the resource usage is limited at runtime, not at compile-time or start of execution time.
If BOOK_NUM
was extremely big (eg a billion) you could have an issue (program won't be runnable because of lack of memory).
If BOOK_NUM
was slightly small (e.g. a dozen) you could have an issue in running some cases (not enough space for books).
If you (wrongly!) declared book library[BOOK_NUM];
as some local variable (e.g. in main
), the call frame should be small enough (because the entire call stack is limited to a few mega-bytes, so individual call frames should not exceed a few kilobytes) so BOOK_NUM
should be kept small (a few dozens at most).
To quote the GNU coding standards:
4.2 Writing Robust Programs
Avoid arbitrary limits on the length or number of any data structure, including file names, lines, files, and symbols, by allocating all data structures dynamically
So a better way could be to have:
typedef struct book_st {
char* name;
char* authors;
char* publisher;
char* genre;
int year;
int num_pages;
int copies;
} book;
then a "making function" (or "constructing" function) like
/* returns a freshly allocated book to be deleted by delete_book;
the strings arguments should be not null and are duplicated. */
book* make_book(const char*n, const char*a, const char*p,
const char*g, int y, int np, int c) {
assert (n != NULL);
assert (a != NULL);
assert (p != NULL);
assert (g != NULL);
book* b = malloc(sizeof(book));
if (!b) { perror("malloc book"); exit(EXIT_FAILURE); };
memset (b, 0, sizeof(book)); // useless, but safe
char* pname = strdup(n);
if (!pname) { perror("strdup name"); exit(EXIT_FAILURE); };
char* pauth = strdup(a);
if (!pauth) { perror("strdup author"); exit(EXIT_FAILURE); };
char *ppub = strdup(p);
if (!ppub) { perror("strdup publisher"); exit(EXIT_FAILURE); };
char *pgenre = strdup(g);
if (!pgenre) { perror("strdup genre"); exit(EXIT_FAILURE); };
b->name = pname;
b->authors = pauth;
b->publishers = ppub;
b->genre = pgenre;
b->year = y;
b->num_pages = np;
b->copies = c;
return b;
}
Notice that every call to malloc
should be tested, because malloc
could fail. Here I just exit with some error message; in some cases you would want to recover from malloc
failure (e.g. a server might want to continue processing future requests), but that is boringly difficult (you might need to free
any unseless malloc
-ed pointer so far, etc...).
Of course, you need a destroying or deleting function to release memory, like:
/* destroy and free a book obtained by make_book */
void delete_book(book*b) {
if (!b) return;
free (b->name), b->name = NULL;
free (b->authors), b->authors = NULL;
free (b->publisher), b->publisher = NULL;
free (b->genre), b->genre = NULL;
free (b);
}
Notice my defensive programming style. I am clearing the malloc
-ed book
pointer before filling it; I am setting to NULL
every pointer field in book
just after free
-ing it. In principle both are useless.
BTW, you could make your library a struct
ending with a flexible array member:
struct library_st {
unsigned size; // allocate size
unsigned nbbooks; // actual number of books
book* books[]; // actually, size slots
};
and have functions like struct library_st*make_library(unsigned s);
and struct library_st*add_book(struct library_st*lib, book*book);
which would return perhaps an updated and reallocated library.
The main thing in C is to document the memory allocation discipline. Every function should say (at least in a comment) who is in charge of freeing pointers and how.
Read much more (at least for concepts and terminology) about virtual address space, C dynamic memory allocation, memory leaks, garbage collection. Notice that reference counting is not a silver bullet.
Consider using Linux as your primary development environment on your laptop. It has good tools (gcc -Wall -g -fsanitize=address
with a recent GCC, gdb
, valgrind, Boehm's conservative GC ...) and lots of free software whose source code is worth studying to learn more about C programming.
BTW, to store your library on the disk, consider serialization techniques (and textual formats à la JSON), or perhaps sqlite or some real database (PostGreSQL, MongoDB, ...)