如何修复 "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
行的片段放入一个预定大小的小缓冲区数组中,然后根据需要strcat
和realloc
内存来存储该行作为字符串,*(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
(或 realloc
或 calloc
,您将不会默默地分配错误数量的内存) 电话。
我正在尝试编写一个将文本文件转换为 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
行的片段放入一个预定大小的小缓冲区数组中,然后根据需要strcat
和realloc
内存来存储该行作为字符串,*(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
(或realloc
或calloc
,您将不会默默地分配错误数量的内存) 电话。