我有一个应该接收输入的程序,记住最长的字符串并在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;
}
答案 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()
。相反,请将p
和line
初始化为NULL
,并仅在需要时重新分配。没有必要为1 char
分配空间;这可以在需要存储第一个字符时完成。此外,不需要在C中转换malloc()
的结果(C ++中的不同故事);并且,sizeof char
始终为1,因此这是多余的,只会使代码混乱。
发布代码中的基本问题似乎是在读取字符时,count
会递增。然后,如果此字符是空格或换行符,则count
可能不为0,因此可能无法满足具有释放条件的退出。而不是复杂的条件,重新考虑程序的流程。
读取一个字符后(除非遇到EOF
),如果该字符是字母,则count
应递增,p
应重新分配。如果此步骤成功,则该字符应存储在p[]
中,然后应该以空值终止。
否则,如果字符是\n
或空格,则max
应与count
进行比较。如果count
较大,则应重新分配line
。如果此步骤成功,则p
指向的字符串应复制到line[]
。然后max
的值为count
,count
重置为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()
函数调用之后的调试期间;你检查了指针p
和line
的值,你仍然看到它指向的值(字符串)。如果是这样,那不是内存泄漏,因为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)