【问题标题】:How to fix "realloc(): invalid pointer"如何修复“realloc():无效指针”
【发布时间】:2019-08-15 04:30:40
【问题描述】:

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

文件如下所示:

Jake Ali Maria
24 23 43
Montreal Johannesburg Sydney

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

Jake, 24, Montreal
...etc

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

我想避免硬编码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 错误。缓冲区加载正常,只有当我在 for 循环中为 i=1 调用 'realloc' 时才会崩溃,当我到达第二行时。

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

我真的很感激这方面的帮助,我整天都被困住了。

非常感谢!

***编辑

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

***编辑

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

【问题讨论】:

  • 您分配了line(这是用词不当,因为它不是一行),它是指向三个char*s 的指针。你永远不会初始化line 的内容(也就是说,你永远不会让这三个char*s 中的任何一个指向任何地方)。因此,当您执行realloc(*(line + i), ...) 时,第一个参数是未初始化的垃圾。
  • 你需要初始化line中的值。另外,不要使用*(line + i),而是使用line[i],这样更清晰。
  • void load_and_convert 无法将lines 返回给调用者。它所做的只是创建一个 memory-leak
  • #define CHUNK 10 是你自己最大的敌人。为什么?它违反了基本规则永远不要吝啬缓冲区大小!最好给自己一次阅读一行的最佳机会,而不是零碎阅读。当您真正开始将数据列转换为.csv 输出的行时,这将变得非常明显。您需要在每个lines[i] 指针指向的内存块中存储一行数据,然后为.csv 输出构建另一个char**。 (提示:您的 CHUNK-1 测试未正确处理验证)
  • 此外,您是否会拥有一个每行字段多于行的数据文件?如果是这样,您将需要提前仔细计划。包含的数据文件,例如"Jake Ali Maria Sam""24 23 43 31" 等...将比恰好具有相同数量的字段和行的数据文件花费更多的精力来处理。

标签: c dynamic-memory-allocation realloc


【解决方案1】:

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

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

另外,*(line+i) = (char *)realloc(*(line+i), ...) 仍然很糟糕,因为如果realloc 分配内存失败,它将返回一个空指针,破坏*(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 &lt;stdlib.h&gt; 的错误。
  • sizeof(char) 根据定义是 1 个字节。
  • 在分配内存时,养成使用T* p = malloc(n * sizeof *p); 而不是T* p = malloc(n * sizeof (T)); 的习惯会更安全。这样,如果p 的类型发生变化,如果您忽略更新malloc(或realloccalloc)调用,您就不会默默地分配错误的内存量。

【讨论】:

    【解决方案2】:

    在这里,您必须将指针数组归零(例如使用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) 更易读(语义相同)。

    【讨论】:

    • 严格来说,calloc 不能保证将指针值初始化为空指针。 (不保证空指针由所有位为零表示。)您可以通过将每个元素显式初始化为 NULL 来保证可移植性。
    • @P. Gillich:realloc(NULL, ...)malloc 一样好,但这不是你在做的。您将一些随机的未初始化值传递给realloc
    • @P.Gillich 在for 循环的开头,是的。
    • 您要求的是真实世界的系统,对我来说,这意味着现实世界中实际存在(或存在)的系统。我没有声称它们是通用系统。
    • 该设置仅适用于当前会话,因此您不会打扰任何人。此外,还有strcat(*(line+i), buffer);这行,会导致编译错误。
    猜你喜欢
    • 2021-04-18
    • 1970-01-01
    • 1970-01-01
    • 2013-03-21
    • 2017-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-30
    相关资源
    最近更新 更多