我认为有一些与内存和堆损坏相关的问题使我的程序无法正常运行(主要是因为它内部的一些bug)。程序停止运行,或者在退出后崩溃。
我试图学习树是如何工作的,在我的例子中,我必须编写一个交叉引用器来读取文档中的所有单词(在我的例子中,是输入行),以及每个单词出现的行号列表。
foo
bar bar
foo bar
应产生以下产出:
2 foo: [1, 3]
2 bar: [2, 3]
其中[]中的数字表示单词所在的行。
我的代码有两个主要问题:
- 它只在方括号内打印1,就好像程序从不检查换行符一样
- 如果我试图运行超过10行的输入,它就会崩溃。没有gdb,它允许我输出所有我想要的行,并且直到它达到10行时才会崩溃:
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 ()
我甚至没有输入quit(中断循环的单词),它就停止了,给了我这个警告。
我不知道如何解决这个问题,因为我可能忘记释放一些东西(有一些堆分配),但我不知道问题可能在哪里。
这是密码:
#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函数根本不返回'\n'吗?
1条答案
按热度按时间ss2ws0br1#
free(p)
时,没有必要将p->word
和p->lines
设置为NULL
。addtree()
:* 的优先级高于+。它应该是:但是当你增加p-〉计数之前的行时你只需要:
在对
calloc()
的调用中也存在类似的逻辑错误。valgrind在我修复了这两个问题后很高兴,我不能用10行输入重现崩溃。
getword()
:行号不前进,因为您从最后一行跳过\n
:然而调用方期望单词[0]为“\n”以推进行号
n
。以下是一个最小修复:并且输出现在是:
也就是说,我建议您让调用者阅读一行
fgets()
,然后使用getwork()
的修订版本将该行拆分为单词。1.(未固定)
getword()
:除非您真的希望在每种情况下以不同的方式处理输入,否则您3次单独调用getch()
是有问题的。addtree()
:您当前记录了给定单词的重复行,但似乎希望后续重复行无效。此外,可能还不如使用malloc()
而不是calloc()
,因为您在后面显式设置了p->lines
。对于sizeof()
,首选使用变量而不是类型。并且输出现在是:
1.(未修复)如果
realloc()
失败,p = realloc(p, ...)
泄漏p。1.(未修复)
malloc()
、calloc()
、strdup()
可能会失败并返回NULL。您需要检查: