C中的内存泄漏,很可能是由于realloc

时间:2017-10-11 18:32:51

标签: c memory-leaks realloc

我有一个应该接收输入的程序,记住最长的字符串并在EOF打印出来。我的代码可以工作,但是当通过调试器运行时,会检测到内存泄漏。我在Windows中编译,没有像Valgrind这样的调试器,所以我没有得到关于错误的更多信息。我能想象的唯一可能导致这个泄漏的是realloc()或free()函数。但是,我在C方面不够熟练,无法理解问题所在。

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

int main(void)
{
    char *p;
    char *line;
    int sc;
    p = (char*) malloc ( sizeof(char) );
    line = (char*) malloc ( sizeof(char) );
    int count = 0;
    int max = 0;
    p[count] = 0;
    while ( ( sc = getchar()) != EOF ) {
        if ( p == NULL ) {
            p = (char*) realloc ( p, sizeof(char) );
        }
        if ( isalpha(sc) ) {
            p[count] = sc;
            count++;
            p = (char*) realloc( p, (count+1)*sizeof(char) );
            p[count] = 0;
        } else if ( sc == '\n' || sc == ' ' ) {
            if ( count > max ) {
                line = (char*) realloc( line, (count+1)*sizeof(char) );
                strcpy( line, p );
                max = count;
            } else if ( count == 0) {
                printf("%d characters in longest word: %s\n", max, line);
                free(line);
                free(p);
                break;
            }
            count = 0;
        }
    }
    return 0;
}

3 个答案:

答案 0 :(得分:2)

您声明in the comments您“已尝试将free()移至程序结尾”,但我不相信您。问题似乎是这是一个设计糟糕的程序,并不总是达到free()语句,因为当sc是空格或换行符时,count不大可能为0,之后count重置为0,下一个字符将被读入sc(不太可能是空格或换行符)。

只需将调用移至free()到程序的末尾即可修复内存泄漏,这是Valgrind报告的:

λ> valgrind --tool=memcheck ./a.out
==2967== Memcheck, a memory error detector
==2967== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==2967== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==2967== Command: ./a.out
==2967== 
this is a test
==2967== 
==2967== HEAP SUMMARY:
==2967==     in use at exit: 10 bytes in 2 blocks
==2967==   total heap usage: 14 allocs, 12 frees, 42 bytes allocated
==2967== 
==2967== LEAK SUMMARY:
==2967==    definitely lost: 10 bytes in 2 blocks
==2967==    indirectly lost: 0 bytes in 0 blocks
==2967==      possibly lost: 0 bytes in 0 blocks
==2967==    still reachable: 0 bytes in 0 blocks
==2967==         suppressed: 0 bytes in 0 blocks
==2967== Rerun with --leak-check=full to see details of leaked memory
==2967== 
==2967== For counts of detected and suppressed errors, rerun with: -v
==2967== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

但是代码还有很多其他问题。首先,永远不要将对realloc()的调用结果存储在指针的唯一副本中,而是指向要重新分配的内存;可能会返回空指针,如果是这样,则会出现内存泄漏和丢失数据。而是使用临时变量来保存结果,并仅在检查空指针后将此值分配给原始指针。如果返回了空指针,最简单的解决方案可能是使用错误消息终止程序,如下所示;只要处理错误,就可以用其他方式处理错误。

发布的代码比需要的更复杂,多次调用malloc()realloc()。相反,请将pline初始化为NULL,并仅在需要时重新分配。没有必要为1 char分配空间;这可以在需要存储第一个字符时完成。此外,不需要在C中转换malloc()的结果(C ++中的不同故事);并且,sizeof char始终为1,因此这是多余的,只会使代码混乱。

发布代码中的基本问题似乎是在读取字符时,count会递增。然后,如果此字符是空格或换行符,则count可能不为0,因此可能无法满足具有释放条件的退出。而不是复杂的条件,重新考虑程序的流程。

读取一个字符后(除非遇到EOF),如果该字符是字母,则count应递增,p应重新分配。如果此步骤成功,则该字符应存储在p[]中,然后应该以空值终止。

否则,如果字符是\n或空格,则max应与count进行比较。如果count较大,则应重新分配line。如果此步骤成功,则p指向的字符串应复制到line[]。然后max的值为countcount重置为0.

循环结束后,仅当输入中有单词时才会打印结果。然后在程序终止之前进行解除分配。

来自isalpha()的{​​{1}}函数和类似函数需要ctype.h(或int)范围内的unsigned char值。通常,您需要将这些函数的参数值强制转换为EOF,以避免未定义的行为。但是,在这种情况下,由于unsigned char返回getchar()(或int)范围内的unsigned char值,因此不需要广播。

您也可以考虑使用EOF函数代替isspace()。这将允许其他空白字符(例如sc == '\n' || sc == ' ')分隔输入中的单词。如在OP中所写,'\t'(其中"one\tword"是制表符)的输入将导致输出:

'\t'

以下是已发布代码的修改版本:

7 characters in longest word: oneword

以下是Valgrind的健康清单:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

int main(void)
{
    char *p = NULL;
    char *line = NULL;
    int sc;
    int count = 0;
    int max = 0;

    while ((sc = getchar()) != EOF) {
        if (isalpha(sc)) {
            ++count;                                    // read a letter
            char *temp = realloc(p, count + 1);         // +1 for '\0'
            if (temp == NULL) {                         // check allocation
                perror("Failure to reallocate p");
                exit(EXIT_FAILURE);
            }
            p = temp;                                   // OK to reassign p
            p[count-1] = sc;                            // store character
            p[count] = 0;                               // add null-terminator
        } else if (isspace(sc)) {
            if (count > max) {
                char *temp = realloc(line, count + 1);  // +1 for '\0'
                if (temp == NULL) {                     // check allocation
                    perror("Failure to reallocate line");
                    exit(EXIT_FAILURE);
                }
                line = temp;                            // OK to reassign line
                strcpy(line, p);
                max = count;
            }
            count = 0;
        }
    }

    if (max > 0) {
        printf("%d characters in longest word: %s\n", max, line);        
    } else {
        puts("No words in input");
    }

    free(line);
    free(p);

    return 0;
}

答案 1 :(得分:1)

我认为在free()函数调用之后的调试期间;你检查了指针pline的值,你仍然看到它指向的值(字符串)。如果是这样,那不是内存泄漏,因为free()不会更改指针的值或在其中分配0'\0'字符。 free()只需释放内存块,以便下次调用任何内存分配函数时,它会将该内存作为可用内存并进行分配。因此,在致电free()后,我们始终将NULL分配给指针p = NULL;

更正下面的代码并尝试: -

free(line);
free(p);
line = NULL;
p = NULL;
break;

答案 2 :(得分:0)

根据您的编码方法,我将使用calloc(您需要)代替malloc来摆脱它:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

int main(void)
{
    char *p = NULL;
    char *line = NULL;
    int sc;
    p = calloc ( sizeof(char), 1);
    line = calloc ( sizeof(char),1 );
    size_t count = 0;
    size_t max = 0;
    p[count] = 0;
    while ( ( sc = getchar()) != EOF ) {
        if ( p == NULL ) {
            p = realloc ( p, sizeof(char) );
        }
        if ( isalpha(sc) ) {
            p[count] = (char)sc;
            count++;
            p = realloc( p, (count+1) * sizeof(char) );
            p[count] = 0;
        } else if ( sc == '\n' || sc == ' ' ) {
            if ( count > max ) {
                line = realloc( line, (count+1)*sizeof(char) );
                strcpy( line, p );
                max = count;
            } else if ( count == 0) {
                printf("%zu characters in longest word: %s\n", max, line);
                free(line);
                free(p);
                break;
            }
            count = 0;
        }
    }
    return 0;
}

使用Valgrind输出:

==4362== Memcheck, a memory error detector
==4362== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==4362== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==4362== Command: ./program
==4362== 
Hello World

5 characters in longest word: Hello
==4362== 
==4362== HEAP SUMMARY:
==4362==     in use at exit: 0 bytes in 0 blocks
==4362==   total heap usage: 15 allocs, 15 frees, 2,096 bytes allocated
==4362== 
==4362== All heap blocks were freed -- no leaks are possible
==4362== 
==4362== For counts of detected and suppressed errors, rerun with: -v
==4362== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

现在尝试了解,为什么不使用malloc以及为什么使用calloc

修改

你确定这是泄漏吗?并不是一个未初始化的价值? 我是说因为如果你输入一封信很好,问题就是数字,因此calloc消化了。

这是Valgrind的输出,使用您的代码而无需修改:

==5042== Memcheck, a memory error detector
==5042== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==5042== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==5042== Command: ./program
==5042== 
1   
==5042== Conditional jump or move depends on uninitialised value(s)
==5042==    at 0x4E88CC0: vfprintf (vfprintf.c:1632)
==5042==    by 0x4E8F898: printf (printf.c:33)
==5042==    by 0x40082B: main (in /home/michael/program)
==5042==  Uninitialised value was created by a heap allocation
==5042==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5042==    by 0x400715: main (in /home/michael/program)
==5042== 
0 characters in longest word: 
==5042== 
==5042== HEAP SUMMARY:
==5042==     in use at exit: 0 bytes in 0 blocks
==5042==   total heap usage: 4 allocs, 4 frees, 2,050 bytes allocated
==5042== 
==5042== All heap blocks were freed -- no leaks are possible
==5042== 
==5042== For counts of detected and suppressed errors, rerun with: -v
==5042== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)