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

时间:2017-12-28 16:52:26

标签: c segmentation-fault getline strsep

我在运行以下代码时遇到分段错误。

它应该基本上读取超过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;
}

我想它与内存分配有关,但无法弄清楚如何修复它。

2 个答案:

答案 0 :(得分:6)

getlinestrsep的组合经常会引起混淆,因为两个函数都会将指针传递给指针作为初始参数。如果再次将通过strsep的指针传递给getline,则在第二次迭代时会冒未定义行为的风险。

考虑一个示例:getline将101个字节分配给line,并将100个字符的字符串读入其中。请注意,len现在设置为101.您调用strsep,在字符串中间找到'|',因此它将line指向过去的line+50 getline 1}}。现在再次致电len。它看到另一个100个字符的行,并得出结论可以将它复制到缓冲区,因为line仍然是101.但是,由于line现在指向缓冲区的中间,所以写100字符变成未定义的行为。

在致电strsep之前复制while (getline(&line, &len, data)>0) { char *copy = line; p = strsep(&copy, "|"); printf("Line number: %lu \t p: %s\n", count, p); count++; }

line

传递给getline的{​​{1}}现在在循环迭代之间保留。

答案 1 :(得分:1)

查看表达式getline(&line, &len, data)并阅读manpage

  

如果* line设置为NULL并且* len在调用之前设置为0,则          getline()将分配一个缓冲区来存储该行。这个缓冲区          即使getline()失败,也应该被用户程序释放。

第一次循环时应该是这种情况(虽然我们无法看到len声明的位置,但我们只是假设您的真实代码正确执行此操作)

  

或者,在调用getline()之前,*行可以包含一个          指向malloc(3)分配的缓冲区* len字节的指针。如果          缓冲区不足以容纳线,getline()调整它的大小          使用realloc(3),根据需要更新* line和* len。

好的,所以如果line != NULL它必须指向由大小为malloc的{​​{1}}分配的缓冲区。第一次调用len时分配的缓冲区(如上所述)满足此要求。

请注意,getline指向缓冲区并不够好,它必须是开头。

现在查看表达式line并阅读manpage

  

...通过用a覆盖分隔符来终止此令牌          空字节(&#39; \ 0&#39;),*行更新为指向令牌

因此,第一个参数(strsep(&line,"|")已更改,以便您可以使用相同的第一个参数再次调用line,并获取 next 令牌。这意味着strsep不再是line的有效参数,因为它不是getline缓冲区的开头(长度为malloc现在也错了。)

在实践中,要么

  1. len会尝试将getline个字节读入您提供的缓冲区中,但由于您按照第一个令牌的长度提前len,它会写下已分配的结尾块。这可能只会损坏堆而不是立即死亡
  2. line会尝试重新分配你给它的缓冲区,但由于它不是一个有效的分配块,你会再次受到堆损坏。
  3. 虽然我们在这里,但您也不会检查getline是非NULL,但是损坏p是主要问题。

    哦,如果您认为问题与分配有关,请尝试使用line - 它通常会发现事情首先出错。