如何在 main() 之外分配结构?

how to allocate a struct outside the main()?

我构建了一个 book 结构,如下所示:

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] 定义库时遇到内存问题 write/read,所以我决定分配。

问题是,它只允许我在主函数内部进行分配。 当我写下这一行时:

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() 里面它就可以了。这是为什么?

另外,什么是定义数组的更好方法,以便我以后可以用其他函数更改它?

你可以声明一个全局变量或静态变量,假设 BOOK_NUM 是一些 #define-d constant (例如 #define BOOK_NUM 100 在之前的某处你的代码):

 book library[BOOK_NUM];

但是,堆分配通常更可取,因为资源使用在运行时受到限制,而不是在 compile-time 或执行开始时。

如果 BOOK_NUM 非常大(例如十亿),您可能会遇到问题(由于内存不足,程序将无法运行)。

如果 BOOK_NUM 稍微小一点(例如一打),您可能会在 运行 某些情况下遇到问题(space 买书不够)。

如果您(错误地!)将 book library[BOOK_NUM]; 声明为某些 local 变量(例如在 main 中),调用框架应该足够小(因为整个 call stack 仅限于几个 mega-bytes,因此单个调用帧不应超过几千字节)因此 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

所以更好的方法是:

typedef struct book_st {
  char* name;
  char* authors;
  char* publisher;
  char* genre;
  int year;
  int num_pages;
  int copies;
} book;

然后像

这样的“制作功能”(或“构造”功能)
/* 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 任何 unseless malloc-ed 指针到目前为止,等等...)。

当然需要销毁或删除函数来释放内存,如:

/* 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 指针;我在 free 之后将 book 中的每个指针字段设置为 NULL。原则上两者都没有用。

顺便说一句,你可以让你的图书馆成为 structflexible 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); 之类的功能,这些功能 return 也许是更新和重新分配的库。

C 中的主要内容是记录内存分配规则。 每个函数 应该说明(至少在注释中)谁负责释放指针以及如何释放指针。

阅读更多关于 virtual address space, C dynamic memory allocation, memory leaks, garbage collection. Notice that reference counting is not a silver bullet 的内容(至少在概念和术语方面)。

考虑使用 Linux 作为笔记本电脑上的主要开发环境。它有很好的工具(gcc -Wall -g -fsanitize=address 和最近的 GCC, gdb, valgrind, Boehm's conservative GC ...) and lots of free software 其源代码值得研究以了解更多有关 C 编程的信息。

顺便说一句,要将您的库存储在磁盘上,请考虑 serialization techniques (and textual formats à la JSON), or perhaps sqlite 或一些真实的数据库(PostGreSQL、MongoDB、...)

您只能在函数内部调用 malloc。 main()是一个函数。您可以编写其他功能。您不能只声明一个全局变量并通过调用函数对其进行初始化。