如何修复 "realloc(): invalid pointer"

How to fix "realloc(): invalid pointer"

我正在尝试编写一个将文本文件转换为 CSV 文件的函数。 输入文件有 3 行,其中包含 space 分隔的条目。我必须找到一种方法将一行读入一个字符串,并将输入文件中的三行转换为 CSV 文件中的三列。

文件如下所示:

Jake Ali Maria
24 23 43
Montreal Johannesburg Sydney

我必须把它改造成这样的东西:

Jake, 24, Montreal
...etc

我想我可以创建一个 char **line 变量来保存对三个单独 char 数组的三个引用,一个对应于输入文件的三行中的每一行。即,我的目标是 *(line+i) 存储文件的第 i+1 行。

我想避免对 char 数组大小进行硬编码,例如

char line1 [999]; 
fgets(line1, 999, file);

所以我写了一个while循环将fgets行的片段放入一个预定大小的小缓冲区数组中,然后根据需要strcatrealloc内存来存储该行作为字符串,*(line+i) 作为指向字符串的指针,其中 i 是第一行的 0,第二行的 1,等等。

这里是有问题的代码:

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

#define CHUNK 10

char** getLines (const char * filename){
    FILE *file = fopen(filename, "rt");
    char **lines = (char ** ) calloc(3, sizeof(char*));
    char buffer[CHUNK];
    for(int i = 0; i < 3; i++){
        int lineLength = 0;
        int bufferLength = 0;
        *(lines+i) = NULL;
        do{
            fgets(buffer, CHUNK, file);
            buffLength = strlen(buffer);
            lineLength += buffLength;
            *(lines+i) = (char*) realloc(*(lines+i), (lineLength +1)*sizeof(char));
            strcat(*(lines+i), buffer);
        }while(bufferLength ==CHUNK-1);
    }
    puts(*(lines+0));
    puts(*(lines+1));
    puts(*(lines+2));

    fclose(file);
}

void load_and_convert(const char* filename){
    char ** lines = getLines(filename);
}

int main(){
    const char* filename = "demo.txt";
    load_and_convert(filename);
}

这仅适用于 i=0。但是,使用 GDB 进行此操作时,我发现出现了 realloc(): invalid pointer 错误。缓冲区加载正常,只有当我在 i=1 的 for 循环中调用 'realloc' 时才会崩溃,当我到达第二行时。

我在一个小例子中设法存储了我想要的字符串,我试图查看发生了什么,但输入都在同一行上。也许这与 fgets 从新行读取有关?

我真的很感激能得到一些帮助,我已经被困了一整天了。

非常感谢!

***编辑

我尝试按照建议使用 calloc 而不是 malloc 来初始化变量 **lines,但我仍然有相同的 issue.I 已将修改添加到我上传的原始代码。

***编辑

删除文件重新编译后,上面的似乎可以工作了。感谢大家帮助我!

在这里,您必须将指针数组归零(例如使用 calloc()),

char **line = (char**)malloc(sizeof(char*)*3); //allocate space for three char* pointers

否则重新分配

*(line+i) = (char *)realloc(*(line+i), (inputLength+1)*sizeof(char)); //+1 for the empty character

使用未初始化的指针,导致未定义的行为。 它与 i=0 一起工作纯属巧合,是遇到 UB 时的典型陷阱。

此外,在使用strcat()时,您必须确保第一个参数已经是一个以零结尾的字符串!这里不是这种情况,因为在第一次迭代时,realloc(NULL, ...); 给你留下了一个未初始化的缓冲区。这可能会导致 strcpy() 写入已分配缓冲区的末尾并导致堆损坏。一个可能的解决方法是使用 strcpy() 而不是 strcat() (这在这里应该更有效率):

   do{
        fgets(buffer, CHUNK, file);
        buffLength = strlen(buffer);
        lines[i] = realloc(lines[i], (lineLength + buffLength + 1));
        strcpy(lines[i]+lineLength, buffer);
        lineLength += buffLength;
    }while(bufferLength ==CHUNK-1);

如果行(包括换行符)恰好 CHUNK-1 字节长,检查 bufferLength == CHUNK-1 将不会执行您想要的操作。更好的检查可能是 while (buffer[buffLength-1] != '\n').

顺便说一句。 line[i]*(line+i)(在语义上相同)可读性更好。

您分配了 line(这是用词不当,因为它不是单行),它是指向三个 char* 的指针。您永远不会初始化 line 的内容(也就是说,您永远不会让这三个 char* 中的任何一个指向任何地方)。因此,当您执行 realloc(*(line + i), ...) 时,第一个参数是未初始化的垃圾。

要使用realloc进行初始内存分配,其第一个参数必须是空指针。您应该先将 line 的每个元素显式初始化为 NULL

此外,*(line+i) = (char *)realloc(*(line+i), ...)仍然很糟糕,因为如果realloc分配内存失败,它将return一个空指针,破坏*(line + i),并泄漏旧指针.相反,您应该将其拆分为单独的步骤:

char* p = realloc(line[i], ...);
if (p == null) {
    // Handle failure somehow.
    exit(1);
} 
line[i] = p;

更多注意事项:

  • 在 C 中,您应该避免转换 malloc/realloc/calloc 的结果。没有必要,因为 C 允许从 void* 到其他指针类型的隐式转换,并且显式可能会掩盖您不小心省略 #include <stdlib.h>.
  • 的错误 根据定义,
  • sizeof(char) 是 1 个字节。
  • 分配内存时,养成使用 T* p = malloc(n * sizeof *p); 的习惯比使用 T* p = malloc(n * sizeof (T)); 更安全。这样,如果 p 的类型发生变化,如果您忽略更新 malloc(或 realloccalloc,您将不会默默地分配错误数量的内存) 电话。