2014-10-02 76 views
0

我需要從給定的字符串或單詞中刪除標點符號。這裏是我的代碼:爲什麼我不能釋放內存?(調試錯誤)

void remove_punc(char* *str) 
{ 
    char* ps = *str; 
    char* nstr; 
    // should be nstr = malloc(sizeof(char) * (1 + strlen(*str))) 
    nstr = (char *)malloc(sizeof(char) * strlen(*str)); 
    if (nstr == NULL) { 
     perror("Memory Error in remove_punc function"); 
     exit(1); 
    } 
    // should be memset(nstr, 0, sizeof(char) * (1 + strlen(*str))) 
    memset(nstr, 0, sizeof(char) * strlen(*str)); 
    while(*ps) { 
     if(! ispunct(*ps)) { 
      strncat(nstr, ps, 1); 
     } 
     ++ps; 
    } 
    *str = strdup(nstr); 
    free(nstr); 
} 

如果我的主要功能是簡單的一個:

int main(void) { 
    char* str = "Hello, World!:)"; 
    remove_punc(&str); 
    printf("%s\n", str); 
    return 0; 
} 

它的工作原理!輸出是Hello World

現在我想讀取一個大文件並從文件中刪除標點符號,然後輸出到另一個文件。 這裏的另一個主要功能:

int main(void) { 
    FILE* fp = fopen("book.txt", "r"); 
    FILE* fout = fopen("newbook.txt", "w"); 
    char* str = (char *)malloc(sizeof(char) * 1024); 
    if (str == NULL) { 
     perror("Error -- allocating memory"); 
     exit(1); 
    } 
    memset(str, 0, sizeof(char) * 1024); 
    while(1) { 
     if (fscanf(fp, "%s", str) != 1) 
      break; 
     remove_punc(&str); 
     fprintf(fout, "%s ", str); 
    } 
    return 0; 
} 

當我在Visual C重新運行該程序++,它報告 Debug Error! DAMAGE: after Normal Block(#54)0x00550B08, 和中止程序。

所以,我必須調試代碼。一切正常,直到執行free(nstr)陳述。 我感到困惑。任何人都可以幫助我?

+0

'strlen'不包含終止符,因此使用* no *標點符號發送字符串到該函數將保證一個覆蓋錯誤,調用未定義的行爲,並且如果您'重新*幸運*,崩潰你的過程。 – WhozCraig 2014-10-02 05:03:00

+0

對原始文本進行復制並將其寫入新文件並不是那麼有效,最好是從原始文本中讀取,然後將字符寫入char,然後跳過任何標點符號。這樣你可以節省內存分配。 – 2014-10-02 05:49:11

+0

我需要閱讀一個詞,並從該詞中刪除標點符號,然後統計該書中的詞。 – wintr 2014-10-02 06:08:11

回答

2

您忘記了空終止符的malloc空間。更改

nstr = (char *)malloc(sizeof(char) * strlen(*str)); 

nstr = malloc(strlen(*str) + 1); 

注意casting malloc is a bad idea,如果你要malloc然後memset到零,你可以使用calloc,而不是其做到了這一點。


你的程序後面還有一個bug。 remove_punc函數將str更改爲指向一個新分配的緩衝區,該緩衝區對於沒有標點符號的字符串來說足夠大。然而,你然後循環到fscanf(fp, "%s", str)。這不再讀入一個1024字節的緩衝區,它正在讀入前面無標點符號的字符串的緩衝區大小。

所以,除非你的文件包含全部以長度遞減順序排列的行(在去除標點符號後),否則你將在這裏引起緩衝區溢出。您需要重新考慮您的循環設計。例如,也許你可以讓remove_punc保持輸入不變,並返回一個指向新分配的字符串的指針,打印後你將輸入free

如果你使用這個解決方案,然後使用%1023s來避免fscanf發生緩衝區溢出(遺憾的是沒有簡單的方法在這裏取一個變量,而不是硬編碼長度)。使用帶有"%s"的scanf函數與gets一樣危險。

+0

謝謝!它解決了我的問題。但它仍然無法正確運行一個大文件。最糟糕的是,IDE沒有給出提示。 – wintr 2014-10-02 05:12:26

+0

@wintr現在學會使用你的調試器來找出它出錯的地方;和/或添加額外的打印語句,以便您可以看到發生了什麼。 – 2014-10-02 05:21:04

1

@MatMcNabb的答案解釋了您的問題的原因。我將建議幾種方法來簡化代碼,並使其不易受內存問題的影響。

  1. 如果性能不是問題,請逐個字符讀取文件並丟棄打孔字符。

    int main(void) 
    { 
        FILE* fp = fopen("book.txt", "r"); 
        FILE* fout = fopen("newbook.txt", "w"); 
        char c; 
    
        while ((c = fgetc(fp)) != EOF) 
        { 
         if (!ispunct(c)) 
         { 
         fputc(c, fout); 
         } 
        } 
    
        fclose(fout); 
        fclose(fp); 
    
        return 0; 
    } 
    
  2. 通過使在輸入字符串以及輸出字符串到remove_punc最小化呼叫到mallocfree數。

    void remove_punc(char* inStr, char* outStr) 
    { 
        char* ps = inStr; 
        int index = 0; 
        while(*ps) 
        { 
         if(! ispunct(*ps)) 
         { 
         outStr[index++] = *ps; 
         } 
         ++ps; 
        } 
        outStr[index] = '\0'; 
    } 
    

    ,並改變你在main使用remove_punc的方式。

    int main(void) 
    { 
        FILE* fp = fopen("book.txt", "r"); 
        FILE* fout = fopen("newbook.txt", "w"); 
        char inStr[1024]; 
        char outStr[1024]; 
    
        while (fgets(inStr, 1024, fp) != NULL) 
        { 
         remove_punc(inStr, outStr); 
         fprintf(fout, "%s", outStr); 
        } 
    
        fclose(fout); 
        fclose(fp); 
    
        return 0; 
    } 
    
0

在你的主,你有以下

char* str = (char *)malloc(sizeof(char) * 1024); 
... 
     remove_punc(&str); 
... 

你remove_punc()函數的str中的地址,但是當你在remove_punc功能做到這一點

... 
*str = strdup(nstr); 
... 

你沒有將新字符串複製到先前分配的緩衝區中,你正在重新分配str指向新線大小的緩衝區!這意味着,當您從文件中讀取行並且要讀取的下一行比上一行更長時,您將遇到麻煩。

您應該單獨保留原始緩衝區,而不是返回包含新字符串的新分配緩衝區,例如返回nstr,然後釋放它,當它完成後或更好,只是將原始文件逐字節複製到新文件並排除任何標點符號。這會更有效