如何在main()之外分配一个struct?

时间:2016-01-14 08:42:27

标签: c dynamic-memory-allocation

我构建了一个看起来像这样的书结构:

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]这样的库时,内存写入/读取有问题,所以我决定分配。

事实是,它只允许我在main函数内部进行分配。 当我写这一行:

book *library = (book*)malloc(BOOK_NUM*sizeof(book));
main()之外的

,它给了我一个错误:

  

IntelliSense:常量表达式中不允许函数调用

     

错误C2099:初始化程序不是常量

但是如果我将上面一行移到main()里面就行了。那是为什么?

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

2 个答案:

答案 0 :(得分:6)

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

 book library[BOOK_NUM];

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

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

如果BOOK_NUM略小(例如十几个),那么在运行某些案例时可能会出现问题(书籍空间不足)。

如果你(错误地!)将book library[BOOK_NUM];声明为某些本地变量(例如在main中),则调用帧应该足够小(因为整个{{3}限制为几个兆字节,因此单个调用帧不应超过几千字节)因此BOOK_NUM应保持较小(最多几十个)。

引用call stack

  

4.2编写健壮的程序

     

通过动态分配所有数据结构,避免对任何数据结构的长度或数量进行任意限制,包括文件名,行,文件和符号

所以更好的方法是:

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到目前为止任何无意义的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);
}

注意我的GNU coding standards风格。在填写之前,我正在清除malloc - ed book指针;我在NULL之后book设置了free每个指针字段。原则上两者都没用。

顺便说一句,您可以将您的图书馆设为struct,以defensive programming成员结尾:

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中的主要内容是记录内存分配规则。 每个函数应该说(至少在评论中)谁负责释放指针以及如何。

阅读更多关于flexible arrayvirtual address spaceC dynamic memory allocationmemory leaks的内容(至少有关于概念和术语的内容)。请注意,garbage collectionreference counting

考虑将Linux用作笔记本电脑上的主要开发环境。它有很好的工具(gcc -Wall -g -fsanitize=address包含最近的not a silver bulletgdbGCCvalgrind ...)和大量Boehm's conservative GC来源代码值得学习以了解有关C编程的更多信息。

顺便说一句,要将您的库存储在磁盘上,请考虑free software技术(和文本格式àlaserialization),或者JSON或某些真实数据库(PostGreSQL,MongoDB ,. ..)

答案 1 :(得分:2)

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

相关问题