C getline()/strep()组合导致分段错误
运行下面的代码时,我遇到了一个分段错误 它基本上应该读取一个超过3M行的C getline()/strep()组合导致分段错误,c,segmentation-fault,getline,strsep,C,Segmentation Fault,Getline,Strsep,运行下面的代码时,我遇到了一个分段错误 它基本上应该读取一个超过3M行的.csv文件,然后做其他事情(与问题无关),但在207746次迭代后,它返回一个分段错误。如果我删除p=strep(&line,“|”)只需打印整个行即可打印>3M行 int ReadCSV (int argc, char *argv[]){ char *line = NULL, *p; unsigned long count = 0; FILE *data; if (argc <
.csv
文件,然后做其他事情(与问题无关),但在207746次迭代后,它返回一个分段错误。如果我删除p=strep(&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;
}
int-ReadCSV(int-argc,char*argv[]{
char*line=NULL,*p;
无符号长计数=0;
文件*数据;
如果(argc<2)返回1;
if((数据=fopen(argv[1],“r”))==NULL){
printf(“无法打开CSV文件”);
出口(0);
}
while(getline(&line,&len,data)>0){
p=strep(&line,“|”);
printf(“行号:%lu\t p:%s\n”,计数,p);
计数++;
}
自由线;
fclose(数据);
返回0;
}
我想这可能与内存分配有关,但无法找到解决方法。将
getline
和strep
组合使用通常会导致混淆,因为这两个函数都会更改指针,您可以将指针作为初始参数传递给它们。如果再次将通过strep
的指针传递到getline
,则在第二次迭代中可能会出现未定义的行为
考虑一个例子:getline
将101个字节分配给line
,并将100个字符的字符串读入其中。请注意,len
现在设置为101。您调用<代码> StrSEP,它在字符串的中间找到<代码> '>代码>,因此它指向<代码>行<代码> >以前使用的代码>代码行+ 50代码>代码>。现在再次调用getline
。它看到另一行100个字符,并得出结论,可以将其复制到缓冲区中,因为len
仍然是101。但是,由于行
现在指向缓冲区的中间,因此写入100个字符成为未定义的行为
在调用strep
之前,复制行
:
while (getline(&line, &len, data)>0) {
char *copy = line;
p = strsep(©, "|");
printf("Line number: %lu \t p: %s\n", count, p);
count++;
}
现在,传递给
getline
的line
将在循环迭代之间保留。查看表达式getline(&line,&len,data)
,然后阅读:
如果在调用之前*line设置为NULL且*len设置为0,则
getline()将分配一个缓冲区来存储该行。这个缓冲区
即使getline()失败,也应该由用户程序释放
这应该是您第一次循环时的情况(尽管我们看不到len
是在哪里声明的,但让我们假设您的真实代码正确地执行了此操作)
或者,在调用getline()之前,*行可以包含
指向malloc(3)分配的缓冲区*len字节大小的指针。如果
缓冲区不够大,无法容纳该行,getline()将调整其大小
使用realloc(3),根据需要更新*line和*len
好的,如果line!=NULL
它必须指向由大小为len
的malloc
分配的缓冲区。第一次调用getline
(如上所述)所分配的缓冲区满足这一点
注意,行
指向缓冲区的某个地方不够好,它必须是开始
现在查看表达式strep(&line,“|”)
,并阅读以下内容:
。。。此标记通过使用
空字节('\0'),并且*行被更新为超过标记的点
因此,第一个参数(line
)被更改,以便您可以使用相同的第一个参数再次调用strep
,并获取下一个标记。这意味着line
不再是getline
的有效参数,因为它不是malloc
'd缓冲区的开始(并且len
的长度现在也是错误的)
实际上,也不是这样
getline
将尝试将len
字节读入您提供给它的缓冲区,但由于您将line
提升到第一个令牌的长度,因此它会注销分配的块的末尾。这可能只是破坏堆,而不是立即死亡getline
将尝试重新锁定您分配给它的缓冲区,但由于它不是有效的已分配块,您将再次受到堆损坏p
是否为非空,但损坏行是主要问题
哦,如果您认为问题与分配有关,请尝试使用valgrind
——它通常会在第一次出错时发现问题。第207746行是否至少有一个分隔符?如果strsep()没有,则其行为会有所不同。strsep
修改其第一个参数,因此您将丢失第一次分配给您的缓冲区getline
的开头。不要这样做,如果必须使用strep
,请使用单独的临时文件。并检查p
是否不为空。而len
在哪里声明?调用getline
之前是否为零?