为什么我不能释放内存?(调试错误)

时间:2014-10-02 04:59:36

标签: c free

我需要从给定的字符串或单词中删除标点符号。这是我的代码:

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)被执行。 我有点迷惑不解了。有人可以帮帮我吗?

3 个答案:

答案 0 :(得分:2)

您忘记了为null终结符设置malloc空间。改变

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

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

请注意casting malloc is a bad idea,如果您要malloc然后memset为零,则可以使用calloc代替那样做。


您的计划稍后会出现另一个错误。 remove_punc函数将str更改为指向新分配的缓冲区,该缓冲区对于没有标点符号的字符串来说足够大。但是,然后循环到fscanf(fp, "%s", str)。这不再读入1024字节的缓冲区,而是只读取前一个无标点字符串的缓冲区大小。

因此,除非您的文件包含所有行的长度降序(删除标点符号后),否则会导致缓冲区溢出。你需要重新考虑这个循环的设计。例如,您可能让remove_punc保持输入不变,并返回指向新分配字符串的指针,打印后您将free

如果你使用这个解决方案,那么使用%1023s来避免使用fscanf缓冲区溢出(遗憾的是,这里没有简单的方法来获取变量而不是硬编码长度)。使用裸"%s"的scanf函数与gets一样危险。

答案 1 :(得分: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. 通过将输入字符串和输出字符串传递给malloc,最大限度地减少对freeremove_punc的调用次数。

    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';
    }
    

    并更改remove_punc中使用main的方式。

    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;
    }
    

答案 2 :(得分:0)

在你的主要内容中你有以下

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

你的remove_punc()函数获取str的地址但是当你在remove_punc函数中执行此操作时

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

您没有将新字符串复制到先前分配的缓冲区,您正在重新分配str以指向新行大小的缓冲区!这意味着当您从文件中读取行并且要读取的下一行比上一行更长时,您将遇到麻烦。

您应该单独保留原始缓冲区,例如返回包含新字符串的新分配缓冲区,例如返回nstr,然后释放它,当它完成或更好时,只需将原始文件逐字节复制到新文件并排除任何标点符号。那会更有效