2016-01-14 1 views
0

Я построил книгу структуру, которая выглядит следующим образом:Как выделить структуру за пределами main()?

typedef struct _book{ 
    char name[NAME_LENGTH]; 
    char authors[AUTHORS_NAME_LENGTH]; 
    char publisher[PUBLISHER_NAME_LENGTH]; 
    char genre[GENRE_LENGTH]; 
    int year; 
    int num_pages; 
    int copies; 
}book; 

я пытаюсь определить библиотеку, которая представляет собой массив из книг, так что в дальнейшем я мог депонировать книги в библиотеке с другой функцией. имел проблемы с записью/чтением памяти при определении библиотеки вроде этого library[BOOK_NUM], поэтому я решил выделить.

вещь, это только позволяет мне выделить внутри основной функции. , когда я пишу эту строку:

book *library = (book*)malloc(BOOK_NUM*sizeof(book)); 

вне main() он дает мне ошибку:

IntelliSense: function call is not allowed in a constant expression

error C2099: initializer is not a constant

но если я двигаться выше линии, чтобы быть внутри main() она работает. почему это?

также, что лучший способ определить массив, чтобы я мог позже его изменить с помощью других функций?

+0

* Где * вы пишете эту декларацию? Как глобальная переменная? В функции? Можете ли вы создать [Минимальный, полный и проверенный пример] (http://stackoverflow.com/help/mcve) и показать нам? –

+0

Вам просто нужно установить его внутри функции. Это динамическая инициализация. –

+2

О, и [в C вы не должны бросать возврат 'malloc'] (http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc) (или любую другую функцию возвращающий 'void *'). –

ответ

6

Вы могли бы объявить глобальную или статическую переменную, предполагая 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, ...)

+0

#define BOOK_NUM 50. У меня возникли проблемы при попытке определить: книжная библиотека [BOOK_NUM]; вот почему я попытался выделить. но также были проблемы с распределением, когда malloc был за пределами main() –

+0

, когда я определил библиотеку книг [BOOK_NUM]; это дало мне некоторые проблемы с другими функциями, когда я попытался поместить значения в массив –

+1

rtucan, определение глобального массива книг в порядке (это может быть даже разумное дизайнерское решение). Он должен просто работать. С какими «проблемами с другими функциями» вы столкнулись? –

2

Вы можете только позвонить malloc внутри функции. main() - это функция. Вы можете написать другие функции. Вы не можете просто объявить глобальную переменную и инициализировать ее, вызвав функцию.