[英]Weird Segmentation Fault when doing strcpy the 2nd time on the same string C
我正在用逗號分隔由逗號分隔的行,並且由於某些標題中包含逗號,因此將其用引號引起來。 我對第一個引號進行標記,然后將其存儲在fronttoken中,然后對第二個引號進行標記,將其存儲在標題中,然后最后對它進行標記,直到\\ n。 我不明白為什么。
它與strsep有關嗎? 我之所以沒有使用strtok是因為,如果字符串由多個分隔符“,”組成,而它們之間沒有任何內容,我想捕獲空標記。
由於tempStr已充分分配了必要的數量,因此作為strcpy的* est應該足夠。 我已經堅持了幾個小時。 如果有人指出我的錯誤,我將不勝感激。 謝謝。
int main(int argc, char * argv[])
{
char* one = "hello, my, name, is, code monkey, \"This, is a title\", more, random, stuff\n";
char* two = "blah blah blah";
char* tempStr= malloc(1000);
void* freeTempStr = tempStr;
strcpy(tempStr, one);
char* fronttoken = strsep(&tempStr, "\"");
char* title = strsep(&tempStr, "\"");
char* backtoken = strsep(&tempStr, "\n");
char* token;
strcpy(tempStr, fronttoken);
token = strsep(&tempStr, ",");
while (token != NULL)
{
printf("Front tokens: %s\n", token);
token = strsep(&tempStr, ",");
}
printf("Title: %s\n", title);
strcpy(tempStr, backtoken);
token = strsep(&tempStr, ",");
while (token != NULL)
{
printf("Back tokens: %s\n", token);
token = strsep(&tempStr, ",");
}
//2nd strcpy gives segmentation fault
free(freeTempStr)
return 0;
}
輸出...
Front tokens: hello Front tokens: my Front tokens: name Front tokens: is Front tokens: code monkey Front tokens: Title: This, is a title Segmentation fault
在仔細研究了代碼並進行了編譯和試驗之后,我同意MM的分析 ,即緊迫的問題是您試圖復制到空指針。
但是,我懷疑您總體上沒有仔細考慮內存管理。 當你做strcpy(tempStr, one);
,將源字符串復制到分配的內存中。 不幸的是,您分配的內存遠遠超過了所需的副本。 當您隨后執行strcpy(tempStr, fronttoken);
,要復制的fronttoken
正本后的位置one
在tempStr
。 然后,您將其拆分。
您崩潰是因為在tempStr
設置為null時停止,然后嘗試strcpy(tempStr, backtoken)
復制到null指針。
如果解決此問題,則可能會遇到字符串復制重疊的問題。 您當前的反向令牌集足夠小,不會出現問題,但是如果您有100字節的反向令牌,則將有重疊的字符串副本和未定義的行為。
此代碼顯示了問題並解決。 請注意,它在標記周圍包括方括號,因此更容易確切地找到所找到的內容。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char* one = "hello, my, name, is, code monkey, \"This, is a title\", more, random, stuff\n";
char* tempStr= malloc(1000);
void* freeTempStr = tempStr;
strcpy(tempStr, one);
printf("tempStr = [%p .. %p)\n", (void *)tempStr, (void *)(tempStr + 1000));
char* fronttoken = strsep(&tempStr, "\"");
printf("tempStr = %p; fronttoken = %p\n", (void *)tempStr, (void *)fronttoken);
char* title = strsep(&tempStr, "\"");
printf("tempStr = %p; title = %p\n", (void *)tempStr, (void *)title);
char* backtoken = strsep(&tempStr, "\n");
printf("tempStr = %p; backtoken = %p\n", (void *)tempStr, (void *)backtoken);
char* token;
printf("tempStr = %p; fronttoken = %p - before strcpy 1\n", (void *)tempStr, (void *)fronttoken);
strcpy(tempStr, fronttoken);
token = strsep(&tempStr, ",");
while (token != NULL)
{
printf("Front tokens: %p [%s]\n", (void *)token, token);
token = strsep(&tempStr, ",");
}
printf("Title: [%s]\n", title);
printf("tempStr = %p; backtoken = %p - before strcpy 2 (unfixed)\n", tempStr, backtoken);
tempStr = freeTempStr;
printf("tempStr = %p; backtoken = %p - before strcpy 2 (fixed - but beware overlap)\n", tempStr, backtoken);
strcpy(tempStr, backtoken);
token = strsep(&tempStr, ",");
while (token != NULL)
{
printf("Back tokens: %p [%s]\n", (void *)token, token);
token = strsep(&tempStr, ",");
}
free(freeTempStr);
return 0;
}
示例輸出(運行macOS High Sierra 10.13.1的Mac,已安裝安全更新2017-001 — macOS 10.13.1 (17B1002)
,如果尚未安裝,請安裝它!):
tempStr = [0x7fb91ac02880 .. 0x7fb91ac02c68)
tempStr = 0x7fb91ac028a3; fronttoken = 0x7fb91ac02880
tempStr = 0x7fb91ac028b4; title = 0x7fb91ac028a3
tempStr = 0x7fb91ac028ca; backtoken = 0x7fb91ac028b4
tempStr = 0x7fb91ac028ca; fronttoken = 0x7fb91ac02880 - before strcpy 1
Front tokens: 0x7fb91ac028ca [hello]
Front tokens: 0x7fb91ac028d0 [ my]
Front tokens: 0x7fb91ac028d4 [ name]
Front tokens: 0x7fb91ac028da [ is]
Front tokens: 0x7fb91ac028de [ code monkey]
Front tokens: 0x7fb91ac028eb [ ]
Title: [This, is a title]
tempStr = 0x0; backtoken = 0x7fb91ac028b4 - before strcpy 2 (unfixed)
tempStr = 0x7fb91ac02880; backtoken = 0x7fb91ac028b4 - before strcpy 2 (fixed - but beware overlap)
Back tokens: 0x7fb91ac02880 []
Back tokens: 0x7fb91ac02881 [ more]
Back tokens: 0x7fb91ac02887 [ random]
Back tokens: 0x7fb91ac0288f [ stuff]
請注意,使用調試器還可以使您很快找到此問題。
您的代碼未按發布要求進行編譯; 缺少分號。 此外,缺少許多標准標頭。
在具有gcc 6.2.6的64位Ubuntu 16上,發生崩潰的地方不是您所觀察的地方。 由於缺少<string.h>
標頭,因此strsep
函數被隱式聲明為返回int
,這是錯誤的。 因此, fronttoken
變量收到一個垃圾值,並且第一個strcpy
失敗。
要做的第一件事是獲得沒有錯誤或警告的干凈構建(在打開編譯器用戶社區廣泛建議的任何診斷后)。
修復所有問題之后,您將遇到一個簡單的邏輯問題:
while (token is not null) {
token = strsep(&tempStr, ...)
}
strcpy into tempStr
由於while
循環不包含break語句,因此終止的唯一方法是token
變為null。
token
變為null的唯一方法是strsep
返回null。
根據文檔, strsep
返回null的唯一方法是tempStr
為null。
即token
為null的事實證明tempStr
必須為null,這意味着我們一定不能將tempStr
用作strcpy
的目的地。
tempStr
變為null的方式是在上一次對strsep
調用中,未找到令牌定界符。 在這種情況下, strsep
將整個字符串作為提取的令牌返回,並用null覆蓋指針。
換句話說,在從字符串中提取了最后一個標記之后, strsep
用null覆蓋指針。 然后,下次調用strsep
,它返回null,表示“沒有更多可用的令牌”。 這使strsep
易於使用:只需繼續調用它,直到得到null。 但是您必須了解臨時上下文指針在該過程中會被清空。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.