使用 struct for c 分配内存错误
Allocation memory error with use struct for c
我写了一个管理图书馆的代码;编译完成了,但是在模拟过程中我得到了一个 Allocation error (case2)
并且我不知道为什么。
第一种情况可以正常工作,但如果我在第一种情况下输入了多个名称,则第二种情况将无法正常工作。
我做错了什么?我希望我已经足够清楚了。
typedef struct {
char name[80];
char **books;
int books_num;
} Subscription;
int main() {
// Variables declaration:
int option = 0, subs_num = 0, i = 0, books_num = 0;
Subscription *subs_library;
char **books;
char subs_new_name[80], book_new_name[80];
printf("Choose an option\n");
do {
scanf("%d", &option);
switch (option) {
case 1:
printf("Case 1: enter a new name\n");
scanf("%s", subs_new_name);
if (subs_num == 0) {
subs_library = malloc(sizeof(Subscription));
} else {
subs_library = realloc(subs_library, sizeof(Subscription));
}
strcpy(subs_library[subs_num].name, subs_new_name);
subs_library[subs_num].books_num = 0;
subs_num++;
printf("ADDED\n");
break;
case 2:
printf("Case 2: enter the book name\n");
scanf("%s", book_new_name);
if (books_num == 0) {
books = malloc(sizeof(char*));
books[books_num] = malloc(80 * sizeof(char));
} else {
books = realloc(books, sizeof(char*));
books[books_num] = malloc(80 * sizeof(char));
}
if (books[books_num] == NULL) {
printf("Allocation Error\n");
exit(1);
}
strcpy(books[books_num], book_new_name);
books_num++;
printf("ADDED\n");
break;
}
} while (option != 7);
return 0;
}
我想问题出在 scanf
只读取一个字符串直到一个分隔符,在你的情况下 - 一个空格分隔输入的多个名称。分隔符之后的字符保留在输入缓冲区中,并立即由对 scanf
.
的其他调用进行处理
您应该考虑使用 getline
来读取名称并检查来自对 scanf
.
的其他调用的 return 值
你的重新分配realloc(books, sizeof(char *))
只分配了一个指针的大小char *
,而不是你需要的扩大数组的大小:
books=realloc(books,sizeof(char*));
您需要将指针的大小 (char *
) 乘以您计划存储在数组中的书籍数量。您维护 books_num
.
中的书籍数量
正如 Joachim Pileborg 所说,对于每个 allocation/reallocation,您希望它比当前大小大一。第一次分配(malloc()
),你要分配一本书,是sizeof(char *)
的1倍。这恰好等同于您现有的代码,这很好。但是重新分配 (realloc()
) 每次都会重新分配相同的大小(只够一个指针使用),因此您不会扩大分配。您需要将一个指针所需的大小 (sizeof(char *)
) 乘以您想要的指针数量,即 books_num + 1
。正如 Joachim 的回答,这是
books = realloc(books, (books_num + 1)*sizeof(char *));
这会将数组 books
的分配再扩大一个指针。然后,在下一行中,您正确分配了一个大小为 80 的字符串。
您的 subs_library
有同样的重新分配问题。
重新分配的频率较低
您可能希望减少调整分配大小的频率。在这种情况下,每次添加条目时都会重新分配。减少重新分配次数的一种简单技术是每次分配大小变满时将分配大小加倍。但是您必须维护分配大小(容量)并在添加内容时检查它。例如:
char **buffer; /* buffer of pointers to char */
int capacity = 1; /* number of elements allocated for */
int size = 0; /* number of elements actually used */
那么初始分配是
/* Initial allocation */
buffer = malloc(capacity*sizeof(*buffer));
并添加一些 char *new_item
到 buffer
/* When adding an element */
if ( size == capacity ) {
/* Double allocation every time */
capacity *= 2;
/* Reallocate the buffer to new capacity */
realloc(buffer, capacity*sizeof(*buffer));
}
/* Item will fit, add to buffer */
buffer[size++] = new_item;
请注意,我使用了 sizeof(*buffer)
而不是 sizeof(char *)
。这使编译器弄清楚类型和大小是什么。这样,如果我出于某种原因更改 buffer
的类型,我就不必更改代码中的更多地方。为简洁起见,我遗漏的另一件事是您应该始终检查 return 值以确保它们不是 NULL
.
问题是您的重新分配调用。比如你做
realloc(books,sizeof(char*))
这会将指向 books
的内存重新分配为 one 指向字符大小的指针,这正是您已经拥有的。这将导致您索引超出分配内存的范围,这是未定义的行为。
如果要分配超过 个 元素,则需要将基类型大小乘以要分配的元素数,例如
realloc(books, (books_num + 1) * sizeof(char *))
您重新分配数组的代码不正确。您没有为新的数组大小分配足够的空间。当您重新分配这些数组时,您传递的是单个元素的大小,因此该数组的长度仍然为 1 而不是 subs_num + 1
。传递给 realloc
的大小应该是元素数量乘以单个元素大小(以字节为单位)。
将 subs_library
和 books
初始化为 NULL
并更改数组重新分配:
if (subs_num == 0) {
subs_library = malloc(sizeof(Subscription));
} else {
subs_library = realloc(subs_library, sizeof(Subscription));
}
进入这个:
subs_library = realloc(subs_library, (subs_num + 1) * sizeof(*subs_library));
并对 books
执行相同的操作,更改:
if (books_num == 0) {
books = malloc(sizeof(char*));
books[books_num] = malloc(80 * sizeof(char));
} else {
books = realloc(books, sizeof(char*));
books[books_num] = malloc(80 * sizeof(char));
}
为此:
books = realloc(books, (books_num + 1) * sizeof(*books));
books[books_num] = malloc(80 * sizeof(char));
或更简单:
books = realloc(books, (books_num + 1) * sizeof(*books));
books[books_num] = strdup(book_new_name);
我写了一个管理图书馆的代码;编译完成了,但是在模拟过程中我得到了一个 Allocation error (case2)
并且我不知道为什么。
第一种情况可以正常工作,但如果我在第一种情况下输入了多个名称,则第二种情况将无法正常工作。
我做错了什么?我希望我已经足够清楚了。
typedef struct {
char name[80];
char **books;
int books_num;
} Subscription;
int main() {
// Variables declaration:
int option = 0, subs_num = 0, i = 0, books_num = 0;
Subscription *subs_library;
char **books;
char subs_new_name[80], book_new_name[80];
printf("Choose an option\n");
do {
scanf("%d", &option);
switch (option) {
case 1:
printf("Case 1: enter a new name\n");
scanf("%s", subs_new_name);
if (subs_num == 0) {
subs_library = malloc(sizeof(Subscription));
} else {
subs_library = realloc(subs_library, sizeof(Subscription));
}
strcpy(subs_library[subs_num].name, subs_new_name);
subs_library[subs_num].books_num = 0;
subs_num++;
printf("ADDED\n");
break;
case 2:
printf("Case 2: enter the book name\n");
scanf("%s", book_new_name);
if (books_num == 0) {
books = malloc(sizeof(char*));
books[books_num] = malloc(80 * sizeof(char));
} else {
books = realloc(books, sizeof(char*));
books[books_num] = malloc(80 * sizeof(char));
}
if (books[books_num] == NULL) {
printf("Allocation Error\n");
exit(1);
}
strcpy(books[books_num], book_new_name);
books_num++;
printf("ADDED\n");
break;
}
} while (option != 7);
return 0;
}
我想问题出在 scanf
只读取一个字符串直到一个分隔符,在你的情况下 - 一个空格分隔输入的多个名称。分隔符之后的字符保留在输入缓冲区中,并立即由对 scanf
.
您应该考虑使用 getline
来读取名称并检查来自对 scanf
.
你的重新分配realloc(books, sizeof(char *))
只分配了一个指针的大小char *
,而不是你需要的扩大数组的大小:
books=realloc(books,sizeof(char*));
您需要将指针的大小 (char *
) 乘以您计划存储在数组中的书籍数量。您维护 books_num
.
正如 Joachim Pileborg 所说,对于每个 allocation/reallocation,您希望它比当前大小大一。第一次分配(malloc()
),你要分配一本书,是sizeof(char *)
的1倍。这恰好等同于您现有的代码,这很好。但是重新分配 (realloc()
) 每次都会重新分配相同的大小(只够一个指针使用),因此您不会扩大分配。您需要将一个指针所需的大小 (sizeof(char *)
) 乘以您想要的指针数量,即 books_num + 1
。正如 Joachim 的回答,这是
books = realloc(books, (books_num + 1)*sizeof(char *));
这会将数组 books
的分配再扩大一个指针。然后,在下一行中,您正确分配了一个大小为 80 的字符串。
您的 subs_library
有同样的重新分配问题。
重新分配的频率较低
您可能希望减少调整分配大小的频率。在这种情况下,每次添加条目时都会重新分配。减少重新分配次数的一种简单技术是每次分配大小变满时将分配大小加倍。但是您必须维护分配大小(容量)并在添加内容时检查它。例如:
char **buffer; /* buffer of pointers to char */
int capacity = 1; /* number of elements allocated for */
int size = 0; /* number of elements actually used */
那么初始分配是
/* Initial allocation */
buffer = malloc(capacity*sizeof(*buffer));
并添加一些 char *new_item
到 buffer
/* When adding an element */
if ( size == capacity ) {
/* Double allocation every time */
capacity *= 2;
/* Reallocate the buffer to new capacity */
realloc(buffer, capacity*sizeof(*buffer));
}
/* Item will fit, add to buffer */
buffer[size++] = new_item;
请注意,我使用了 sizeof(*buffer)
而不是 sizeof(char *)
。这使编译器弄清楚类型和大小是什么。这样,如果我出于某种原因更改 buffer
的类型,我就不必更改代码中的更多地方。为简洁起见,我遗漏的另一件事是您应该始终检查 return 值以确保它们不是 NULL
.
问题是您的重新分配调用。比如你做
realloc(books,sizeof(char*))
这会将指向 books
的内存重新分配为 one 指向字符大小的指针,这正是您已经拥有的。这将导致您索引超出分配内存的范围,这是未定义的行为。
如果要分配超过 个 元素,则需要将基类型大小乘以要分配的元素数,例如
realloc(books, (books_num + 1) * sizeof(char *))
您重新分配数组的代码不正确。您没有为新的数组大小分配足够的空间。当您重新分配这些数组时,您传递的是单个元素的大小,因此该数组的长度仍然为 1 而不是 subs_num + 1
。传递给 realloc
的大小应该是元素数量乘以单个元素大小(以字节为单位)。
将 subs_library
和 books
初始化为 NULL
并更改数组重新分配:
if (subs_num == 0) {
subs_library = malloc(sizeof(Subscription));
} else {
subs_library = realloc(subs_library, sizeof(Subscription));
}
进入这个:
subs_library = realloc(subs_library, (subs_num + 1) * sizeof(*subs_library));
并对 books
执行相同的操作,更改:
if (books_num == 0) {
books = malloc(sizeof(char*));
books[books_num] = malloc(80 * sizeof(char));
} else {
books = realloc(books, sizeof(char*));
books[books_num] = malloc(80 * sizeof(char));
}
为此:
books = realloc(books, (books_num + 1) * sizeof(*books));
books[books_num] = malloc(80 * sizeof(char));
或更简单:
books = realloc(books, (books_num + 1) * sizeof(*books));
books[books_num] = strdup(book_new_name);