簡體   English   中英

在同一字符串C上進行第二次strcpy時出現奇怪的分段錯誤

[英]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正本后的位置onetempStr 然后,您將其拆分。

您崩潰是因為在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM