[英]Heap block warning during the words counting of a program
我认为有一些与 memory 和堆损坏相关的问题不允许我的程序正常运行(主要是因为它内部的一些错误)。 该程序只是停止运行,或在退出后崩溃。
我正在尝试学习树是如何工作的,对于我的情况,我必须编写一个交叉引用器来读取文档中的所有单词(在我的示例中是输入行),以及每个单词的行号列表它发生的地方。 例如:
foo
bar bar
foo bar
应该产生 output:
2 foo: [1, 3]
2 bar: [2, 3]
[] 中的数字是我们的单词所在的行。
我的代码有两个主要问题:
t
t
t
t
t
quit
5 t: [1, 1, 1, 1, 1]
当我用 gdb 运行它时,它给了我这个:
(gdb) r
Starting program: C:\...\6.exe
[New Thread 15276.0x14fc]
t
t
t
warning: HEAP[6.exe]:
warning: Heap block at 000001E191B97CA0 modified at 000001E191B97CB6 past requested size of 6
Thread 1 received signal SIGTRAP, Trace/breakpoint trap.
0x00007ff981f969ff in ntdll!RtlRegisterSecureMemoryCacheCallback () from C:\WINDOWS\SYSTEM32\ntdll.dl
(gdb) bt
#0 0x00007ff981f969ff in ntdll!RtlRegisterSecureMemoryCacheCallback ()
from C:\WINDOWS\SYSTEM32\ntdll.dll
#1 0x00007ff981f9288a in ntdll!RtlZeroHeap () from C:\WINDOWS\SYSTEM32\ntdll.dll
#2 0x00007ff981f61357 in ntdll!EtwLogTraceEvent () from C:\WINDOWS\SYSTEM32\ntdll.dll
#3 0x00007ff981f95839 in ntdll!RtlRegisterSecureMemoryCacheCallback ()
from C:\WINDOWS\SYSTEM32\ntdll.dll
#4 0x00007ff981f4de29 in ntdll!EtwLogTraceEvent () from C:\WINDOWS\SYSTEM32\ntdll.dll
#5 0x00007ff981ed24b7 in ntdll!RtlReAllocateHeap () from C:\WINDOWS\SYSTEM32\ntdll.dll
#6 0x00007ff981ed237a in ntdll!RtlReAllocateHeap () from C:\WINDOWS\SYSTEM32\ntdll.dll
#7 0x00007ff97fb71a89 in ucrtbase!_realloc_base () from C:\WINDOWS\System32\ucrtbase.dll
#8 0x00007ff71ff81bbe in addtree ()
#9 0x00007ff71ff81a4e in main ()
我什至没有输入退出(打破循环的词),它只是通过给我这个警告而停止。
我不知道如何解决这个问题,因为我可能忘记释放一些东西(有一些堆分配),但我不知道问题出在哪里。
这是代码:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 100
#define MAXWORD 100
#define IS_NOISE_WORD(word) \
(strcmp(word, "a") == 0 || \
strcmp(word, "an") == 0 || \
strcmp(word, "the") == 0 || \
strcmp(word, "and") == 0 || \
strcmp(word, "or") == 0 || \
strcmp(word, "in") == 0 || \
strcmp(word, "of") == 0 || \
strcmp(word, "to") == 0 || \
strcmp(word, "is") == 0 || \
strcmp(word, "are") == 0 || \
strcmp(word, "was") == 0 || \
strcmp(word, "were") == 0 || \
strcmp(word, "be") == 0 || \
strcmp(word, "been") == 0 || \
strcmp(word, "being") == 0 || \
strcmp(word, "have") == 0 || \
strcmp(word, "has") == 0 || \
strcmp(word, "had") == 0 || \
strcmp(word, "having") == 0)
/* etc. */
#define IS_NOT_NOISE_WORD(word) (!IS_NOISE_WORD(word))
/* the tree node */
struct tnode {
char *word; /* points to the text */
int count; /* number of occurrences */
int *lines; /* lines where the word occurs */
struct tnode *left; /* left child */
struct tnode *right; /* right child */
};
char buf[BUFSIZE]; /* buffer for ungetch */
int bufp = 0; /* next free position in buf */
/* char *strdup(char *); */
int getword(char *, int);
struct tnode *addtree(struct tnode *, char *, int);
void tfree(struct tnode *);
void treeprint(struct tnode *);
/* word frequency count */
int main(int argc, char *argv[])
{
struct tnode *root = NULL;
char word[MAXWORD];
int n = 1; /* number of lines */
while (getword(word, MAXWORD) != EOF)
{
if (word[0] == '\n')
n++;
/* if there is a word and it's not a noise */
if (isalpha(word[0]) && IS_NOT_NOISE_WORD(word) && strcmp(word, "quit") != 0 && strcmp(word, "exit") != 0)
root = addtree(root, word, n);
if (!strcmp(word, "quit") || !strcmp(word, "exit"))
break;
}
treeprint(root);
tfree(root);
return 0;
}
/* addtree: add a node with the word w at line l, at or below p */
struct tnode *addtree(struct tnode *p, char *w, int l)
{
int cond;
/* a new word has arrived */
if (p == NULL)
{
/* make a new node */
p = malloc(sizeof(struct tnode));
p->word = strdup(w);
p->count = 1;
p->lines = calloc(p->count + 1, sizeof(int));
p->lines[p->count - 1] = l;
p->left = p->right = NULL;
}
else {
cond = strcmp(w, p->word);
if (cond == 0) {
/* repeated word */
p->count++;
p->lines = realloc(p->lines, p->count + 1 * sizeof(int));
p->lines[p->count - 1] = l;
}
else if (cond < 0) {
/* less than into left subtree */
p->left = addtree(p->left, w, l);
}
else {
/* greater than into right subtree */
p->right = addtree(p->right, w, l);
}
}
return p;
}
/* tfree: free a tnode */
void tfree(struct tnode *p)
{
if (p == NULL)
return;
tfree(p->left);
tfree(p->right);
free(p);
if (p->word != NULL) {
free(p->word);
p->word = NULL;
}
if (p->lines != NULL) {
free(p->lines);
p->lines = NULL;
}
}
/* treeprint: in-order print of tree p */
void treeprint(struct tnode *p)
{
int i;
if (p != NULL) {
treeprint(p->left);
printf("%4d %s: [%d", p->count, p->word, p->lines[0]);
for (i = 1; i < p->count; i++)
printf(", %d", p->lines[i]);
printf("]\n");
treeprint(p->right);
}
}
/* getword: get next word or character from input */
int getword(char *word, int lim)
{
char *w = word;
int c, getch(void);
void ungetch(int);
int in_comment = 0; /* 1 if inside a comment */
int in_pp_line = 0; /* 1 if inside a preprocessor line */
int in_string = 0; /* 1 if inside a string */
/* skip spaces */
while (isspace(c = getch()))
;
if (c != EOF)
*w++ = c;
/* not underscore, pp line, comment, string */
if (!isalpha(c) && c != '_' && c != '\"' && c != '#' && c != '/') {
*w = '\0';
return c;
}
if (c == '\"')
in_string = 1;
if (c == '#')
in_pp_line = 1;
/* it only checks single line comments for now */
if (c == '/') {
if ((c = getch()) == '/')
in_comment = 1;
else
ungetch(c);
}
while (--lim > 0)
{
c = getch();
if (in_comment && (c == '\n'))
in_comment = 0;
if (in_pp_line && (c == '\n'))
in_pp_line = 0;
/* if the char is in a string or in a comment or in a pp line, and is not alphanumeric */
if (!isalnum(c) && c != '_' && (in_string == 1 || c != '\"') && !in_pp_line && !in_comment)
{
ungetch(c);
break;
}
if (c == '/' && *(w - 1) == '/')
in_comment = 1;
if (c == '\"')
in_string = (in_string == 1) ? 0 : 1;
*w++ = c;
}
*w = '\0';
return word[0];
}
/* get a (possibly pushed-back) character */
int getch(void) {
return (bufp > 0) ? buf[--bufp] : getchar();
}
/* push character back on input */
void ungetch(int c) {
if (bufp >= BUFSIZE)
printf("ungetch: too many characters\n");
else
buf[bufp++] = c;
}
除了崩溃问题,我不明白为什么 n 计数没有增加。 getword function 根本不返回 '\n' 吗?
free(p)
时,没有必要将p->word
和p->lines
设置为NULL
。void tfree(struct tnode *p) {
if (!p)
return;
tfree(p->left);
tfree(p->right);
if (p->word)
free(p->word);
if (p->lines)
free(p->lines);
free(p);
}
addtree()
:* 的优先级高于 +。 它应该是: p->lines = realloc(p->lines, (p->count + 1) * sizeof(int));
但是当你增加 p->count 之前你只想要的行:
p->lines = realloc(p->lines, p->count * sizeof(int));
在调用calloc()
时也有类似的逻辑错误。
解决这两个问题后,valgrind 很高兴,我无法用 10 行输入重现崩溃。
getword()
:当您从最后一行跳过\n
时,行号不会前进: while (isspace(c = getch()))
但是调用者期望 word[0] 是一个 '\n' 来推进行号n
。 这是一个最小的修复:
do {
c = getch();
} while(c != '\n' && isspace(c));
output 现在是:
3 bar: [2, 2, 3]
2 foo: [1, 3]
也就是说,我建议您让调用者使用fgets()
读取一行,然后将该行拆分为带有修订版getwork()
的单词。
(不固定) getword()
:你 3 次单独调用getch()
是有问题的,除非你真的想在每种情况下以不同的方式处理输入。
addtree()
:您当前记录了给定单词的重复行,但您希望后续的重复行看起来是空操作。 此外,还不如只使用malloc()
而不是calloc()
,因为你在之后显式设置p->lines
。 更喜欢使用变量而不是sizeof()
类型。
struct tnode *addtree(struct tnode *p, char *w, int l) {
if (!p) {
/* make a new node */
p = malloc(sizeof *p);
p->word = strdup(w);
p->count = 1;
p->lines = malloc(sizeof *p->lines);
p->lines[p->count - 1] = l;
p->left = NULL;
p->right = NULL;
return p;
}
int cond = strcmp(w, p->word);
if(cond < 0)
p->left = addtree(p->left, w, l);
else if(!cond && l != p->lines[p->count - 1]) {
p->count++;
p->lines = realloc(p->lines, p->count * sizeof(int));
p->lines[p->count - 1] = l;
} else if(cond > 0)
p->right = addtree(p->right, w, l);
return p;
}
output 现在是:
2 bar: [2, 3]
2 foo: [1, 3]
realloc()
失败p = realloc(p, ...)
会泄漏 p。 它应该是: int *tmp = realloc(p->lines, (p->count + 1) * sizeof(int));
if(!tmp) {
// handle error
return NULL;
}
p->lines = tmp;
malloc()
、 calloc()
、 strdup()
可能会失败并返回 NULL。你想检查一下: p = malloc(sizeof(struct tnode));
if(!p) {
// handle error
return NULL;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.