在运行下面的代码时,我得到了一个分段错误。
它基本上应该读取一个超过3M行的.csv文件,然后再做其他事情(与问题无关),但是经过207746次迭代之后,它会返回一个分段错误。如果我删除p = strsep(&line,"|");并打印整个line,它将打印>3M行。
int ReadCSV (int argc, char *argv[]){
char *line = NULL, *p;
unsigned long count = 0;
FILE *data;
if (argc < 2) return 1;
if((data = fopen(argv[1], "r")) == NULL){
printf("the CSV file cannot be open");
exit(0);
}
while (getline(&line, &len, data)>0) {
p = strsep(&line,"|");
printf("Line number: %lu \t p: %s\n", count, p);
count++;
}
free(line);
fclose(data);
return 0;
}我想这与内存分配有关,但不知道如何修复它。
发布于 2017-12-28 17:01:28
getline和strsep的组合常常会引起混淆,因为这两个函数都会更改指针,将指针作为初始参数传递给它们。如果再次将已通过strsep传递给getline的指针传递给getline,则在第二次迭代中将面临未定义行为的风险。
考虑一个例子:getline将101个字节分配给line,并将100个字符的字符串读入其中。注意,len现在设置为101。您可以调用strsep,它在字符串的中间找到'|',因此它将line指向过去的line+50。现在你又给getline打电话了。它看到另一个100个字符的行,并得出结论,将其复制到缓冲区是可以的,因为len仍然是101。但是,由于line现在指向缓冲区的中间,所以写入100个字符就变成了未定义的行为。
在调用line之前复制strsep
while (getline(&line, &len, data)>0) {
char *copy = line;
p = strsep(©, "|");
printf("Line number: %lu \t p: %s\n", count, p);
count++;
}现在,在循环迭代之间保留传递给line的getline。
发布于 2017-12-28 17:15:25
查看表达式getline(&line, &len, data)并读取命令页
如果*line被设置为NULL,并且*len在调用之前设置为0,那么getline()将分配一个缓冲区来存储该行。即使getline()失败,用户程序也应该释放此缓冲区。
这应该是您第一次循环时的情况(虽然我们无法看到len是在哪里声明的,但让我们假设真正的代码能够正确地做到这一点)
或者,在调用getline()之前,*line可以包含指向malloc(3)-allocated缓冲区*len字节大小的指针。如果缓冲区不够大,不能容纳该行,则getline()使用realloc(3)调整其大小,并根据需要更新* line和*len。
好的,如果是line != NULL,它必须指向大小为len的malloc分配的缓冲区。第一次调用getline (如上面所示)分配的缓冲区满足这一要求。
注意,对于line来说,指向缓冲区的某个地方还不够好,这肯定是开始。
现在看一看表达式strsep(&line,"|"),并阅读命令页
..。此标记通过用空字节('\0')覆盖分隔符而终止,*行被更新以指向标记
因此,更改了第一个参数(line),以便您可以使用相同的第一个参数再次调用strsep,并获得下一个令牌。这意味着line不再是getline的有效参数,因为它不是malloc'd缓冲区的开始(长度len现在也错了)。
在实践中
getline将尝试将len字节读入给它的缓冲区中,但是由于您按照第一个令牌的长度来高级line,所以它会删除分配块的末尾。这可能只会损坏堆,而不是立即死亡。getline将尝试重新分配给它的缓冲区,但是由于它不是一个有效的分配块,您将再次受到堆损坏。当我们在这里的时候,您也不会检查p是非空的,但是破坏line是主要的问题。
哦,如果您认为问题与分配相关,请尝试使用valgrind,它通常会发现事情开始出错的那一刻。
https://stackoverflow.com/questions/48011292
复制相似问题