簡體   English   中英

程序字數統計期間的堆塊警告

[英]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]

[] 中的數字是我們的單詞所在的行。

我的代碼有兩個主要問題:

  • 它只在括號內打印 1,就好像程序從不檢查換行符一樣
  • 如果我嘗試運行 10 行以上的輸入,它就會崩潰。 沒有 gdb 它允許我 output 我想要的所有行,並且在達到 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 ()

我什至沒有輸入退出(打破循環的詞),它只是通過給我這個警告而停止。

我不知道如何解決這個問題,因為我可能忘記釋放一些東西(有一些堆分配),但我不知道問題出在哪里。

這是代碼:

#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' 嗎?

  1. tfree():在釋放指針后引用指針是未定義的行為。 此外,當您free(p)時,沒有必要將p->wordp->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);
}
  1. 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 行輸入重現崩潰。

  1. 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()的單詞。

  1. (不固定) getword() :你 3 次單獨調用getch()是有問題的,除非你真的想在每種情況下以不同的方式處理輸入。

  2. 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]
  1. (不固定)如果realloc()失敗p = realloc(p, ...)會泄漏 p。 它應該是:
    int *tmp = realloc(p->lines, (p->count + 1) * sizeof(int));
    if(!tmp) {
        // handle error
        return NULL;
    }
    p->lines = tmp;
  1. (不固定) malloc()calloc()strdup()可能會失敗並返回 NULL。你想檢查一下:
        p = malloc(sizeof(struct tnode));
        if(!p) {
            // handle error
            return NULL;
        }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM