我构建了一个看起来像这样的书结构:
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:常量表达式中不允许函数调用
错误C2099:初始化程序不是常量
但是如果我将上面一行移到main()
里面就行了。那是为什么?
另外,定义数组的更好方法是什么,以便以后可以用其他函数更改它?
答案 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 array,virtual address space,C dynamic memory allocation,memory leaks的内容(至少有关于概念和术语的内容)。请注意,garbage collection为reference counting。
考虑将Linux用作笔记本电脑上的主要开发环境。它有很好的工具(gcc -Wall -g -fsanitize=address
包含最近的not a silver bullet,gdb
,GCC,valgrind ...)和大量Boehm's conservative GC来源代码值得学习以了解有关C编程的更多信息。
顺便说一句,要将您的库存储在磁盘上,请考虑free software技术(和文本格式àlaserialization),或者JSON或某些真实数据库(PostGreSQL,MongoDB ,. ..)
答案 1 :(得分:2)
您只能在函数内调用malloc。 main()是一个函数。你可以写其他功能。您不能只声明一个全局变量并通过调用函数对其进行初始化。