【问题标题】:"realloc(): invalid next size" when reallocating memory a char** pointer that is a member of a struct“realloc(): invalid next size” 重新分配内存时 char** 指针是结构的成员
【发布时间】:2020-06-28 15:00:13
【问题描述】:

我最近刚开始学习 C,但在计算内存分配时遇到了问题。我花了大约最后 2~3 天的额外时间试图解决这个问题,但还没有找到解决方案。所以首先,我有两个结构:

struct _list {
    // arr is an array of string arrays
    char **arr;
    // recs tracks how many records are in the list
    size_t recs;
    // arrSizes records the size of each string array in arr
    size_t *arrSizes;
};
typedef struct _list list_t;

struct _string {
    char *string;
    // size is used to store strlen
    size_t size;
};
typedef struct _string string_t;

我分别通过以下方式初始化上述结构体。

list_t

list_t *NewList() {
    list_t *List = NULL;
    List = malloc(sizeof(*List));
    if (List == NULL) {
        fprintf(stderr, "Failed to allocate memory to list structure.\n");
        return NULL;
    }
    List->arr = malloc(sizeof(List->arr));
    if (List->arr == NULL) {
        free(List);
        fprintf(stderr, "Failed to allocate memory to list array.\n");
        return NULL;
    }
    List->arrSizes = malloc(sizeof(List->arrSizes));
    if (List->arr == NULL) {
        free(List);
        fprintf(stderr, "Failed to allocate memory to size array.\n");
        return NULL;
    }
    List->recs = 0;
    return List;
}

string_t

// a string array read in by the program is passed with "char* record"
string_t *NewString(char *record)
{
    string_t *String = NULL;
    String = malloc(sizeof * String);
    if (String == NULL) {
        fprintf(stderr, "Failed to allocate memory to string structure.\n");
        return NULL;
    }

    String->size = strlen(record) + 1;
    String->string = malloc(String->size);
    if (String->string == NULL) {
        free(String);
        fprintf(stderr, "Failed to allocate memory to string array.\n");
        return NULL;
    }
    strcpy(String->string, record);
    return String;
}

我从文件中读取行并将它们加载到“匹配结果”缓冲区中,使用类似于以下代码的内容。请忽略退出以及结构初始化完成后我没有空处理的事实;稍后我会添加一些更有用的东西。另外,对长度感到抱歉。我进行了相当多的编辑以生成我能想到的最小示例来重现该问题。

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

// Check if File exists
void FileExists(FILE *FilePath) {
    if (FilePath == NULL) {
        fprintf(stderr, "Error: File not found.\n");
        exit(1);
    }
}

// Delete a string_t struct
int delString(string_t *Structure)
{
    if (Structure != NULL) {
        free(Structure->string);
        free(Structure);
        return 0;
    }
    return 1;
}

// Allocate memory for additional elements added to members of list_t struct
void AllocList(list_t *List, size_t StrLen)
{
    char **ArrStrArr_tmp;
    size_t *SizeArr_tmp;
    char *StrArr_tmp;

    ArrStrArr_tmp = realloc(*List->arr, sizeof(**ArrStrArr_tmp) * List->recs);
    SizeArr_tmp = realloc(List->arrSizes, sizeof(*SizeArr_tmp) * List->recs);
    StrArr_tmp = malloc(sizeof(*StrArr_tmp) * StrLen);

    if ((ArrStrArr_tmp == NULL) || (SizeArr_tmp == NULL)
        || (StrArr_tmp == NULL)) {
        fprintf(stderr, "Failed to allocate memory.\n");
        exit(1);
    }
    else {
        List->arr = ArrStrArr_tmp;
        List->arrSizes = SizeArr_tmp;
        (List->arr)[List->recs-1]= StrArr_tmp;
    }
}

// Add a record to a buffer
int AddRecord(list_t *List, char *AppendRecord)
{
    string_t *line = NewString(AppendRecord);
    List->recs++;

    AllocList(List, line->size);

    (List->arr)[List->recs - 1] = line->string;
    (List->arrSizes)[List->recs - 1] = line->size;

    delString(line);
    return 0;
}

// Sends entire string array to lowercase
void tolowerString(char *UpperString, size_t StrLen)
{
    int i;
    for (i = 0; i < (int)StrLen; i++) {
        UpperString[i] = (char)tolower(UpperString[i]);
    }
}

// Attempt to match string in lines from a file; lines with matches are read into a buffer
int main()
{
    char line[80];
    int PrintedLines = 0;
    list_t *ResultList = NewList();
    char *MyString = "theme";
    char *Filename = "List.txt";
    FILE *in = fopen(Filename, "r");

    // Check if file exists
    FileExists(in);

    while (fscanf(in, "%79[^\n]\n", line) == 1)
    {
        char LookString[80];
        strcpy(LookString, line);
        LookString[strlen(LookString) - 1] = '\0';
        // send lookstring to lowercase
        tolowerString(LookString, strlen(LookString));

        // add line to buffer ResultList if it contains MyString
        if (strstr(LookString, MyString)) {
            AddRecord(ResultList, line);
            PrintedLines++;
        }
    }
    // If PrintedLines is at zero after the while statement terminates, return in abnormal state
    if (PrintedLines == 0) {
        fprintf(stderr, "No matches found. Please check your input if you are sure there is a match.\n");
        return 1;
    }
    fclose(in);
    return 0;
}

当尝试将第 5 条匹配记录读入我的缓冲区时,我的程序在 AllocList 函数的这一行崩溃:

ArrStrArr_tmp = realloc(*List->arr, sizeof(**ArrStrArr_tmp) * List->recs);

我在上面发布的版本中收到以下消息:

realloc(): invalid old size
aborted (core dumped)

我的猜测是,在使用了初始 malloc 中的一些默认内存量后,我遇到了错误,但我不知道究竟是什么原因造成的。在我的实际代码中,我正在打印各种东西(指针大小等),但我仍然无法发现任何东西。奇怪的是,在写这篇文章之前,我居然看到了错误:

realloc(): invalid next size
aborted (core dumped)

但由于某种原因我现在无法重现它......

我还读到,每当我将元素添加到其中一个成员时,我都应该为我的 list_t 结构重新分配内存,但重新分配它实际上并不会改变程序崩溃的位置或方式。无论如何,我不确定我应该如何为我的结构重新分配内存。为了澄清,我的问题是:

  1. 是什么导致了这个内存问题?
  2. 我应该为我的列表结构重新分配内存吗?考虑到我要向 arr 和 arrSizes 成员添加一个额外的元素,我应该重新分配多少内存?

【问题讨论】:

  • 没有仔细阅读您的代码,重新分配 *List-&gt;arr 并将其结果分配给 List-&gt;arr 看起来很不寻常。
  • 在函数AddRecord 中,delString(line); 被调用,line-&gt;string 将在那里被释放,同时将值分配给(List-&gt;arr)[List-&gt;recs - 1]。这看起来很危险。
  • main 函数中的line 是什么?
  • List-&gt;arr = malloc(sizeof(List-&gt;arr));List-&gt;arrSizes = malloc(sizeof(List-&gt;arrSizes)); 看起来很奇怪,因为它们不是根据指向的大小而是根据指针的大小进行分配,但似乎没有害处,因为它们被认为有 0 个元素并且是重新分配的主题。
  • 你应该检查malloc()之后的List-&gt;arrSizes是否是NULL,而不是检查List-&gt;arr两次。

标签: c struct realloc


【解决方案1】:

正如崩溃所暗示的那样,这条线

    ArrStrArr_tmp = realloc(*List->arr, sizeof(**ArrStrArr_tmp) * List->recs);

错了。 这让它读取通过malloc() 分配的未初始化缓冲区,其值是不确定的。

这行的意思是重新分配List-&gt;arr指向的数组,也就是char*的数组。

因此,该行应该是

    ArrStrArr_tmp = realloc(List->arr, sizeof(*ArrStrArr_tmp) * List->recs);

就像下面这行一样,它重新分配了一个size_t 的数组。

    SizeArr_tmp = realloc(List->arrSizes, sizeof(*SizeArr_tmp) * List->recs);

我还发现还有 2 点需要改进:

首先,函数NewList中的一些malloc()的用法不好。 该函数正在创建零元素数组,因此List-&gt;arrList-&gt;arrSizes 不需要空间。 另请注意,realloc() 接受 NULL 作为要重新分配的缓冲区。

list_t *NewList() {
    list_t *List = NULL;
    List = malloc(sizeof(*List));
    if (List == NULL) {
        fprintf(stderr, "Failed to allocate memory to list structure.\n");
        return NULL;
    }
    List->arr = NULL;
    List->arrSizes = NULL;
    List->recs = 0;
    return List;
}

其次,您正在复制 AddRecord 中的指针而不是字符串, 所以你有内存泄漏和潜在的释放后使用的问题。 看来应该复制字符串:

    (List->arr)[List->recs - 1] = line->string;

应该是

    strcpy((List->arr)[List->recs - 1], line->string);

【讨论】:

  • 感谢您的回答!今天晚些时候我会试试这个,然后回复你。一些问题: 1)在你的固定版本我的ArrStrArr_tmp`` realloc statement, why is sizeof(**ArrStrArr_tmp)``` 不正确?这里我打算在我的list_t 结构中为char** arr 添加一个位置,所以我有点惊讶sizeof(*ArrStrArr_tmp)(只有1 个指针级别)是正确的。
  • 2) 在我的结构初始化语句中,最好从初始化将重新分配为 NULL 的成员开始?
  • 3) 我可以重新分配 list_t 结构本身吗?
  • 1) ArrStrArr_tmpchar* 数组的指针(代表字符串)。因此,一个元素的大小(通过sizeof 获得)应该是sizeof(*ArrStrArr_tmp)sizeo(**ArrStrArr_tmp) 不好,因为 **ArrStrArr_tmpchar 并且它的大小通常小于指针。 2)我认为初始化为NULL 更好,因为它会减少。实际上你错误地写了分配大小和变量来检查。 3) 是的,除非您想处理长度将动态更改的list_t 列表,否则您不需要 realloc。
  • 我明白了,所以通过采用 **ArrStrArr_tmp 的大小,我无意中取消了指针的引用并采用了较小的大小...感谢所有答案!
猜你喜欢
  • 1970-01-01
  • 2021-12-12
  • 2020-10-30
  • 1970-01-01
  • 1970-01-01
  • 2020-03-09
  • 1970-01-01
  • 2021-02-14
  • 1970-01-01
相关资源
最近更新 更多