函数在重新分配后不将数据保存到指向结构的指针

Function not saving data to pointer to structure after realloc

此函数应该将数据保存到结构指针动态数组的 library.books_count 实例中。然而事实并非如此。类似的函数 addexistingBooks() 可以完美地完成它。 realloc() 有什么问题?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
    char book_name[32];
    char book_genre[32];
    char author[32];
    int page_count;
    float price;

}Sbook;

typedef struct
{
    char library_name[32];
    Sbook * bookp;
    int books_count;
}Slib;

void menu(char String[50]);
void addexistingBooks(Slib library, int i);
void addBook(Slib library, int i);

int main()
{
    Slib library;
    int i=0;
    char Choice[30];
    printf("Enter amount of books inside the library: ");
    scanf("%d", &(library.books_count));
    library.bookp = (Sbook *)calloc(library.books_count,sizeof (Sbook));
    fflush(stdin);
    addexistingBooks(library, i);

    menu(Choice);
    if(strcmp(Choice,"add")==0)
    {
        addBook(library, i);
    }
    free(library.bookp);
    return 0;
}

void menu(char String[30])
{
    printf("Welcome to the library. If you read about heresy, prepare to be purged \n");
    printf("Please choose a command, by writing the appropriate command: \n");
    printf("1. Write 'add' to add a book. \n");
    printf("2. Write 'remove' to remove a book. \n");
    printf("3. Write 'redact' to redact a book. \n");
    printf("4. Write 'Sort by criteria' to sort the books, where criteria can stand for: 1.bookname, 2.author, 3.genre, 4.price. \n");
    printf("Enter your command: ");
    gets(String);
}

void addexistingBooks(Slib library, int i)
{
    for(i=0;i<library.books_count;i++)
    {
        printf("Enter the name of the book: \n");
        fgets(library.bookp[i].book_name,32,stdin);
        printf("Enter the genre of the book: \n");
        fgets(library.bookp[i].book_genre,32,stdin);
        printf("Enter the author of the book: \n");
        fgets(library.bookp[i].author,32,stdin);
        printf("Enter the page count of the book: \n");
        scanf("%d", &(library.bookp[i].page_count));
        printf("Enter the price of the book: \n");
        scanf("%f", &(library.bookp[i].price));
        fflush(stdin);
    }
}

void addBook(Slib library, int i)
{
        (library.books_count)++;
        realloc(library.bookp,library.books_count);
        fflush(stdin);
        if(library.bookp==NULL)
        {
            exit(1);
        }
        printf("Enter the name of the book: \n");
        fgets(library.bookp[i].book_name,32,stdin);
        printf("Enter the genre of the book: \n");
        fgets(library.bookp[i].book_genre,32,stdin);
        printf("Enter the author of the book: \n");
        fgets(library.bookp[i].author,32,stdin);
        printf("Enter the page count of the book: \n");
        scanf("%d", &(library.bookp[i].page_count));
        printf("Enter the price of the book: \n");
        scanf("%f", &(library.bookp[i].price));
        fflush(stdin);
}

realloc函数的定义,

 void *realloc(void *ptr, size_t size);

因此,您的 realloc 函数:

realloc(library.bookp,library.books_count);

应更改为:

library.bookp = realloc(library.bookp,sizeof(Sbook)*library.books_count);

OT,我在你的menu函数中看到,你使用了gets。这很危险,您应该改用 stdin 中的 fgets。见此linkWhy is the gets function so dangerous that it should not be used?

您的代码有几个错误。

让我们从"non-blocking"错误开始。那些,即使它们真的很关键并且 必须 得到纠正,也不是您遇到崩溃的真正原因。

  • 标准输入的刷新,fflush(stdin);是标准没有定义的东西,所以使用它会导致未定义的行为:在某些环境下它可以工作,在某些其他环境中它无法工作,并且可能存在它 似乎 工作但实际上有害的环境(最差的环境)。建议避开。
  • 函数 gets() 是危险的,因为它不对用户插入的字符串的大小提供任何控制,它的使用 should be avoided.

void addBook() 函数中的问题

  1. 您尝试使用 realloc:

    增加可用 space

    void *realloc(void *ptr, size_t size);

需要原来的指针和新的大小。但是你传递 library.books_count 那只是书的数量。这意味着,如果图书馆曾经包含 4 本书,您尝试仅分配 5 个字节

您需要分配 library.books_count * sizeof(Sbook) 字节。

此外它returns一个新的指针。您需要将其分配给书籍指针:

library.bookp = realloc(library.bookp, library.books_count * sizeof(Sbook));
  1. main() 中你初始化了一个 i 变量,但你从不更新它,因为你直接在 library.books_count.
  2. 中存储了书的数量

然后将它传递给 addexistingBooks(),这是多余的,因为您可以像实际那样使用 library.books_count 本身作为循环。您可以将它用作循环变量,但不需要具有该参数。刚刚

void addexistingBooks(Slib library, )
{
    int i; /* If your C version is C99 or later, you can declare it in the loop itself */

    for(i=0;i<library.books_count;i++)
    {
        /* Omissis */
    }
}

最后你把它传递给 addBook(),它不仅是多余的(因为你可以简单地将新书存储在索引 library.books_count-1,但它是有害的,因为你总是在更新索引0(因为参数i的值为0)。

  1. 虽然可以pass structures to functions as values,但不推荐。第一个原因是你会重载进程的堆栈(整个结构将分配在堆栈区域,这在 PC 应用程序中相当大但在嵌入式系统中非常有限)。第二个原因会给你带来功能上的问题。

实际上,按值传递的参数是传递给函数的变量的副本。这意味着对它们所做的任何更改都不会反映到原始结构中。在您的情况下, library.bookp 指针的更新将在函数外部不可用,导致 (1) 原始结构指向无效的内存地址位置(成为 dangling pointer), (2) 新分配的内存泄漏,没有人能够 free().

通过地址传递结构,而不是使用指向结构的指针addBook() 函数,考虑到 i 参数移除,将变成如下

void addBook(Slib *library)
{
    int i = library->books_count;

    (library->books_count)++;
    library->bookp = realloc(library->bookp, library->books_count * sizeof(Sbook));

    /* and so on... the value of 'i' is now library->books_count-1 */
}

/* Call from main */
int main()
{
    Slib library;

    /* Omissis */

    menu(Choice);
    if(strcmp(Choice,"add")==0)
    {
        addBook(&library, i);
    }
    free(library.bookp);
    return 0;
}