C getline()/strep()组合导致分段错误

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 <

运行下面的代码时,我遇到了一个分段错误

它基本上应该读取一个超过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 < 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(&copy, "|");  
    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
    之前是否为零?