从文件错误中读取单词

Reading words from file error

我在从文件中读取单词并将其传递给二叉树时遇到问题。当我调试它时,它说:

Unhandled exception at 0x76E7773B(ntdll.dll) in Projekt.exe: 0.C00000005:
    Access violation reading location 0x0037902A.

这里是源代码:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

typedef struct Tree {
    int val;
    char *word;
    struct Tree *left;
    struct Tree *right;
} Tree;

void show(Tree *hd) {
    if (hd != NULL) {
        show(hd->left);
        show(hd->right);
        printf("%s -- %d\n", hd->word, hd->val);
    }
}

void zero(Tree *aTree) {
    if (aTree == NULL)
        return;

    zero(aTree->left);
    free(aTree);
    zero(aTree->right);    
}

int alpha(char *word1, char *word2) {
    if (word1[0] == 0 && word2[0] == 0)
        return 2;
    else
    if (word1[0] == word2[0])
        return alpha(&word1[1], &word2[1]);
    else
    if (word1[0] < word2[0])
        return 1;
    else
        return 0;
}

Tree *create(char *word) {
    Tree *temp;
    temp = (Tree*)malloc(sizeof(Tree));
    temp->left = temp->right =  NULL;
    temp->val = 1;
    temp->word = (char*)malloc(sizeof(char));
    strcpy(temp->word, word);
    return temp;
}

Tree *insert(Tree *aTree, char *word) {
    if (aTree == NULL) {
        aTree = create(word);
    } else
    if (alpha(aTree->word, word) == 0) {
        aTree->left = insert(aTree->left,word);
    } else
    if (alpha(aTree->word, word) == 1) {
        aTree->right = insert(aTree->right, word);
    } else
    if (alpha(aTree->word, word) == 2) {
        aTree->val++;
    }
    return aTree;
}

int main(int argc, char *argv[]) {
    Tree *myTree = NULL;
    char buffer[256] = { 0 };
    char temp = 0;
    int i = 0;
    FILE *fp = fopen(argv[1], "r");
    if (fp)  {
        while (temp != EOF) {
            temp = getc(fp);
            temp = toupper(temp);
            if (temp >= 65 && temp <= 90) {
                buffer[i] = temp;
                i++;
            } else {
                if (buffer[0] != 0) {
                    puts(buffer);
                    myTree = insert(myTree, buffer);
                    memset(buffer, 0, sizeof(buffer));
                    i = 0;
                }
            }
        }
    }
    fclose(fp);
    show(myTree);
    return 0;
}

你的程序有几个问题:

  • 在函数 zero 中,你释放指针的时间过早,你应该将 free(aTree); 移动到最后一个语句,否则你会调用未定义的行为,可能会崩溃(但是不是你拥有的那个,因为你从来没有调用过这个函数):

    void zero(Tree *aTree) {
        if (aTree != NULL) {
            zero(aTree->left);
            zero(aTree->right);    
            free(aTree);
    }
    
  • 在函数 alpha 中,您使用递归,其中一个简单的循环就足够了。编译器可能会将其转换为循环,但它确实必须这样做。这不是错误,但为什么不使用更惯用的方法,例如:

    int alpha(const char *word1, const char *word2) {
        for (size_t i = 0;; i++) {
            if (word1[i] == '[=11=]' && word2[i] == '[=11=]')
                return 2;
    
            if (word1[i] == word2[i])
                continue;
    
            if (word1[i] < word2[i])
                return 1;
            else
                return 0;
        }
    }
    
  • 在函数create中,你为字符串分配了一个字节,这肯定是导致崩溃的原因。您应该分配 strlen(word) + 1 或使用 strdup(word)。您也不应该投射 malloc() 的 return 值:

    Tree *create(const char *word) {
        Tree *temp;
        temp = malloc(sizeof(Tree));
        temp->left = temp->right =  NULL;
        temp->val = 1;
        temp->word = strdup(word);
        return temp;
    }
    
  • 在函数 insert 中多次调用 alpha,这是低效的:您可以使用 switch 语句:

    Tree *insert(Tree *aTree, const char *word) {
        if (aTree == NULL) {
            return create(word);
    
        switch (alpha(aTree->word, word)) {
            case 0:
                aTree->left = insert(aTree->left, word);
                break;
            case 1:
                aTree->right = insert(aTree->right, word);
                break;
            case 2:
                aTree->val++;
                break;
            }
        }
        return aTree;
    }
    
  • 函数 main 有多个问题:

    • 您没有检查是否向程序提供了 argv[1]。如果程序 运行 没有命令行参数,它将是 NULL
    • 你对文件结尾的测试是不正确的:temp 应该被定义为 int 并且你应该在用 getc() 从文件中读取字节后测试它的值,它是惯用命名 c 用于此的变量。
    • 您应该使用字符文字而不是硬编码的 ASCII 值。
    • 测试 if (c >= 'A' && c <= 'Z') 适用于 ASCII,它在今天几乎是通用的,但使用 isupper(c) 更可靠。
    • 不需要清除buffer,在插入单词前设置一个'[=35=]'即可。
    • 您还应该检查缓冲区溢出并拒绝处理超过 255 个字符的单词。
    • fpNULL 时,您不应调用 fclose(fp),这是未定义的行为。

    这是更正后的版本:

    int main(int argc, char *argv[]) {
        Tree *myTree = NULL;
        char buffer[256];
        int c;
        size_t i;
        FILE *fp;
    
        if (argc < 2) {
            printf("missing argument\n");
            return 2;
        }
    
        fp = fopen(argv[1], "r");
        if (fp == NULL)  {
            printf("cannot open %s\n", argv[1]);
            return 1;
        }
        i = 0;
        while ((c = getc(fp)) != EOF) {
            c = toupper(c);
            if (isupper(c)) {
                if (i < sizeof(buffer))
                    buffer[i] = c;
                i++;
            } else {
                if (i > 0 && i < sizeof(buffer)) {
                    buffer[i] = '[=14=]';
                    puts(buffer);
                    myTree = insert(myTree, buffer);
                    i = 0;
                }
            }
        }
        fclose(fp);
        show(myTree);
        return 0;
    }