"realloc(): invalid next size" 当重新分配一个作为结构成员的 char** 指针的内存时

"realloc(): invalid next size" when reallocating memory a char** pointer that is a member of a struct

我最近才开始学习 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] = '[=15=]';
        // 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);

我在上面 post 编辑的版本中收到以下消息:

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

我的猜测是,在使用初始 malloc 中的一些默认内存量后,我 运行 陷入了错误,但我不知道究竟是什么导致了这种情况。在我的实际代码中,我正在打印各种东西(指针大小等),但我仍然无法发现任何东西。奇怪的是,在写这个post之前,我居然看到了错误:

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

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

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

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

如崩溃所示,行

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

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

这行的意思是重新分配List->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->arrList->arrSizes 不需要 space。 另请注意,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);