使用struct for c的分配内存错误

时间:2016-01-23 23:26:55

标签: c dynamic-allocation

我写了一个管理图书馆的代码;编译完成但在模拟过程中我获得了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;
}

4 个答案:

答案 0 :(得分:1)

我想问题是scanf只读取一个字符串,直到一个分隔符,在你的情况下 - 一个空格分隔多个输入的名字。分隔符后面的字符保留在输入缓冲区中,并通过对scanf的其他调用立即处理。

您应该考虑使用getline来读取名称,并检查来自scanf的其他来电的返回值。

答案 1 :(得分:1)

重新分配数组的代码不正确。您没有为新阵列大小分配足够的空间。重新分配这些数组时,会传递单个元素的大小,因此数组的长度仍为1而不是subs_num + 1。传递给realloc的大小应该是元素的数量乘以单个元素的大小(以字节为单位)。

subs_librarybooks初始化为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);

答案 2 :(得分:0)

您的重新分配realloc(books, sizeof(char *))仅分配一个指针char *的大小,而不是您需要的放大数组的大小:

    books=realloc(books,sizeof(char*));

您需要将指针(char *)的大小乘以您计划存储在数组中的书籍数量。您保留了books_num中的图书数量。

正如Joachim Pileborg所说,对于每次分配/重新分配,您希望它比当前大小多一个。对于第一次分配(malloc()),您希望为一本书分配,这是sizeof(char *)的1倍。这恰好等同于您现有的代码,这很好。但重新分配(realloc())每次重新分配相同的大小(仅对一个指针足够),因此您不会扩大分配。您需要将一个指针(sizeof(char *))所需的大小乘以所需的指针数,即books_num + 1。正如约阿希姆的回答一样,这是

    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的类型,我就不必更改代码中的更多位置。为简洁起见,我遗漏的另一件事是你应该总是检查返回值,以确保它们不是NULL

答案 3 :(得分:0)

问题是你的重新分配电话。例如,你做

realloc(books,sizeof(char*))

这会将指向books的内存重新分配为一个指向大小字符的指针,这正是您已经拥有的内容。这将引导您索引已分配内存的越界,这是未定义的行为

如果要分配多于一个元素,则需要将基本类型大小与要分配的元素数相乘,例如。

realloc(books, (books_num + 1) * sizeof(char *))