You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单词计数程序Heap块警告与功能异常问题排查求助

单词交叉引用器程序的bug定位与修复

问题概述

编写的单词交叉引用器用于记录输入文本中每个单词的出现行号,预期输入:

foo
bar bar
foo bar

应输出:

2 foo: [1, 3]
2 bar: [2, 3]

但程序存在两个核心问题:

  • 输出的行号始终为1,行号计数器n未正确递增
  • 输入超过一定行数时程序崩溃,gdb调试提示堆块被越界修改

问题定位与修复

1. 行号计数器不递增问题

原因:getword函数开头的循环while (isspace(c = getch())) ;会跳过所有空白字符(包括换行符\n),导致换行符从未被写入word数组,main中if (word[0] == '\n')的判断永远不成立,行号n无法递增。

修复:修改getword函数的空白字符处理逻辑,不跳过换行符:

// 替换原getword开头的跳过空格逻辑
// 跳过除换行外的空白字符
while ((c = getch()) != EOF && isspace(c) && c != '\n')
    ;

if (c != EOF)
    *w++ = c;

2. 堆损坏崩溃问题

存在两处导致堆损坏的错误:

错误1:realloc内存计算优先级错误

在addtree函数中,realloc(p->lines, p->count + 1 * sizeof(int))因乘法优先级高于加法,实际计算为p->count + (1 * sizeof(int)),分配的内存远小于需要的大小,导致越界写入堆内存。

修复:添加括号保证正确的计算顺序:

p->lines = realloc(p->lines, (p->count + 1) * sizeof(int));

错误2:tfree函数释放顺序错误

原tfree先释放p节点,再访问p->word和p->lines,此时p已被释放,属于访问野指针,可能破坏堆结构。

修复:调整释放顺序,先释放节点内的动态内存,再释放节点本身:

void tfree(struct tnode *p)
{
    if (p == NULL)
        return;

    tfree(p->left);
    tfree(p->right);

    if (p->word != NULL) {
        free(p->word);
        p->word = NULL;
    }

    if (p->lines != NULL) {
        free(p->lines);
        p->lines = NULL;
    }

    free(p);
}

修复后的完整代码

#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 */

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

    if (p->word != NULL) {
        free(p->word);
        p->word = NULL;
    }

    if (p->lines != NULL) {
        free(p->lines);
        p->lines = NULL;
    }

    free(p);
}

/* 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 */

    // 跳过除换行外的空白字符
    while ((c = getch()) != EOF && isspace(c) && c != '\n')
        ;

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

内容的提问来源于stack exchange,提问作者coed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 17:51:28