Вы могли бы объявить глобальную или статическую переменную, предполагая BOOK_NUM
некоторые #define
-d константа (например #define BOOK_NUM 100
где-то раньше в коде):
book library[BOOK_NUM];
Однако, выделение кучи, как правило, предпочтительнее, так как использование ресурсов ограничено во время выполнения, а не во время компиляции или времени запуска.
Если BOOK_NUM
был чрезвычайно большой (например, миллиард), у вас могла возникнуть проблема (программа не будет запущена из-за нехватки памяти).
Если BOOK_NUM
был немного небольшим (например, дюжиной), у вас может возникнуть проблема при запуске некоторых случаев (недостаточно места для книг).
Если вы (ошибочно!) Объявил book library[BOOK_NUM];
как некоторые локальной переменной (например, в main
), кадр вызов должен быть достаточно мал (потому что вся call stack ограничивается несколькими мега-байт, поэтому отдельные кадры вызов должен не более нескольких килобайт), поэтому BOOK_NUM
следует хранить небольшим (не более нескольких десятков).
процитировать 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
Так лучший способ мог бы иметь:
то "делает функцию" (или "построения" функции), как
/* 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;
}
Обратите внимание, что каждые звоните по номеру malloc
должен быть протестирован, так как malloc
может потерпеть неудачу. Здесь я просто выхожу с некоторым сообщением об ошибке; в некоторых случаях вы захотите восстановить с malloc
сбой (например,сервер может захотеть продолжить обработку будущих запросов), но это скучно сложно (вам может понадобиться free
любой неудивительный указатель malloc
до сих пор и т. д.).
Конечно, вам нужна разрушающая или удаляемую функция, чтобы освободить память, как:
/* 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);
}
Уведомления моего defensive programming стиля. Я очищаю указатель malloc
-ed book
перед его заполнением; Я устанавливаю NULL
каждое поле указателя в book
сразу после free
-в его. В принципе оба бесполезны.
Кстати, вы могли бы сделать вашу библиотеку struct
заканчивая flexible array членом:
struct library_st {
unsigned size; // allocate size
unsigned nbbooks; // actual number of books
book* books[]; // actually, size slots
};
и имеете такие функции, как struct library_st*make_library(unsigned s);
и struct library_st*add_book(struct library_st*lib, book*book);
который вернется, возможно, обновленная и перераспределена библиотекой.
Главное в C - документировать дисциплину распределения памяти. Каждая функция должна сказать (по крайней мере, в комментарии), кто отвечает за освобождение указателей и как.
Читать гораздо больше (по крайней мере, для понятий и терминологии) о virtual address space, C dynamic memory allocation, memory leaks, garbage collection. Обратите внимание, что reference counting - not a silver bullet.
Рассмотрите возможность использования Linux в качестве основной среды разработки на вашем ноутбуке. Он имеет хорошие инструменты (gcc -Wall -g -fsanitize=address
с недавними GCC, gdb
, valgrind, Boehm's conservative GC ...) и много free software, исходный код которого стоит изучить, чтобы узнать больше о программировании на языке C.
BTW, чтобы сохранить вашу библиотеку на диске, рассмотрим serialization методы (и текстовые форматы, а-ля JSON), или, возможно, sqlite или какой-либо реальной базы данных (PostgreSQL, MongoDB, ...)
* Где * вы пишете эту декларацию? Как глобальная переменная? В функции? Можете ли вы создать [Минимальный, полный и проверенный пример] (http://stackoverflow.com/help/mcve) и показать нам? –
Вам просто нужно установить его внутри функции. Это динамическая инициализация. –
О, и [в C вы не должны бросать возврат 'malloc'] (http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc) (или любую другую функцию возвращающий 'void *'). –