[英]getline() / strsep() combination causes segmentation fault
運行下面的代碼時,我遇到了分段錯誤。
它應該基本上讀取超過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;
}
我想它與內存分配有關,但無法弄清楚如何修復它。
getline
和strsep
組合經常會引起混淆,因為兩個函數都會將指針傳遞給指針作為初始參數。 如果再次將通過strsep
的指針傳遞給getline
,則在第二次迭代時會冒未定義行為的風險。
考慮一個例子: getline
為line
分配101個字節,並在其中讀取一個100個字符的字符串。 請注意, len
現在設置為101.您調用strsep
,它會找到'|'
在字符串中間,使其指向line
到曾經被認為是line+50
。 現在你再次打電話給getline
。 它看到另一個100個字符的行,並得出結論可以將它復制到緩沖區中,因為len
仍然是101.但是,由於line
指向緩沖區的中間,因此寫入100個字符將成為未定義的行為。
在調用strsep
之前復制line
:
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
(如上所述)分配的緩沖區滿足此要求。
注意這不是不夠好, line
至某處點入該緩沖區,它必須是開始。
現在看表達strsep(&line,"|")
和閱讀的手冊頁 為 :
...通過用空字節('\\ 0')覆蓋分隔符來終止此令牌,並更新*行以指向令牌
因此,第一個參數( line
)被更改,以便您可以使用相同的第一個參數再次調用strsep
,並獲取下一個標記。 這意味着line
不再是getline
的有效參數,因為它不是malloc
緩沖區的開頭(長度len
現在也是錯誤的)。
在實踐中,要么
getline
將嘗試將len
個字節讀入您給它的緩沖區,但由於您按line
第一個令牌的長度提前line
,因此它會寫入已分配塊的末尾。 這可能只會損壞堆而不是立即死亡 getline
將嘗試重新分配你給它的緩沖區,但由於它不是一個有效的分配塊,你會再次受到堆損壞。 雖然我們在這里,你也不檢查p
是非NULL,但是破壞line
是主要問題。
哦,如果您認為問題與分配有關,請嘗試使用valgrind
- 它通常會發現事情首先出錯。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.