C中

时间:2015-10-24 14:36:35

标签: c pointers binary-search-tree

我的程序从文件中读取行并使用它构建树。从我的main我调用函数loadFromFile,它完成所有工作。此函数调用insert方法,该方法又调用函数来比较字符串。这是使用的结构:

typedef struct Node {
    char *key;
    int count;
    struct Node *left, *right;
} node;

这是loadFromFile函数,它有两个参数,第一个是读取数据所需的文件地址,第二个是比较字符串的模式。正如您将注意到的,我使用printf稍后向您显示输出:

node * loadFromFile(char * address, int mode){

    FILE * file;
    char * line_readed;
    size_t len = 0;
    ssize_t read;
    node * new_node;
    node * tmp = NULL;

    file = fopen(address, "r");
    if (file == NULL){
        printf("File \"%s\" doesn't exist.\n", address);
        exit(EXIT_FAILURE);
    }

    while ((read = getline(&line_readed, &len, file)) != -1) {
        new_node = get_node();
        line_readed[strlen(line_readed)-1] = 0;
        new_node->key = line_readed;
        printf("line readed: %s\n", line_readed);
        if(tmp != NULL){
            printf("- tmp->key: %s \n", tmp->key);
            tmp = insert(tmp, new_node, mode);
        }else{
            tmp = new_node;
            printf("- tmp->key: %s \n", tmp->key);
        }
    }

    fclose(file);
    printBST(tmp);
    return tmp;
}

要创建新节点,使用函数get_node(),其代码为:

node * get_node() {
    node *temp;
    temp = (node *) malloc(sizeof(node));
    temp -> left = NULL;
    temp -> right = NULL;
    temp -> count = 1;
    return temp;
}

插入的代码就是这个:

node * insert(node *root, node *new_node, int mode) {
    int comparison;
    if(mode){
        comparison = my_strcmp(new_node->key, root->key);
        if (comparison == -1){
            if (root->left == NULL){
                root->left = new_node;
            }else
                insert(root->left, new_node, mode);
        }
        else if (comparison == 1)
            if (root->right == NULL){
                root->right = new_node;
            }else
                insert(root->right, new_node, mode);
        else{
            root->count++;
        }
    }else{
        comparison = my_strignorecasecmp(new_node->key, root->key);   
        if (comparison == -1){
            if (root->left == NULL){
                root->left = new_node;
            }else
                insert(root->left, new_node, mode);
        }
        else if (comparison == 1)
            if (root->right == NULL){
                root->right = new_node;
            }else
                insert(root->right, new_node, mode);
        else{
            root->count++;
        }
    }   
    return root;
}

最后但并非最不重要的是,打印BST的代码:

void printBST(node * root){
    if(root != NULL){
        printBST(root->left);
        printf("%s\n",root->key);
        printBST(root->right);
    }
}

输出如下:

line readed: asddassdfgsdfgdfghx
- tmp->key: asddassdfgsdfgdfghx  
line readed: bb
- tmp->key: bb 
line readed: adassss
- tmp->key: adassss 
line readed: zasx
- tmp->key: zasx 
line readed: www
- tmp->key: www 
www

我独立检查了比较字符串函数,它们工作得很好,所以我假设问题与指针有关,但是我无法看到它在哪里,或者如何解决它。应该是根的变量tmp正在读取我阅读的行的所有值,我不明白为什么;也许是因为我来自JAVA这些东西对我来说有点不同。我不认为问题出在insertprintBST

1 个答案:

答案 0 :(得分:3)

您的问题归结为您使用getline()

getline()查看传入的双指针并检查该地址是否有足够的空间(由len参数指示)来存储下一行。如果传入NULL指针的地址(或者当前缓冲区没有足够的空间用于下一行),它会自动尝试分配(更多)内存。

但是,在您的代码中,永远不会为line_readed分配值:

char *line_readed;

此时line_readed是一个未定义的指针,它可能指向任何地方。使用此指针的值是未定义的行为。

现在你在这个指针上调用getline()getline()将(可能,我们不知道)非NULL未定义line_readed解释为指向剩余零容量的malloc()内存的指针(自len == 0起)。

因此它在未定义的指针上调用realloc()

所有投注都是从这里开始的。你在UB国家。

然而,可能发生的是getline()line_readed分配新内存(然后释放旧指针,导致未定义的行为)。

值得注意的是,如果有足够的空间,getline()首先会尝试写入您传入的地址。由于您的第一个密钥很长,因此缓冲区可能永远不需要在初始({1}}之后调整大小。

如果仔细观察,这也意味着,所有realloc()引用都只会指向相同的内存位置,这也会被新数据重复覆盖。

如果在循环中打印node->key的地址,它可能会在第一次迭代时发生变化,之后就不会发生变化。

您可以尝试使用

line_readed

将字符串从new_node->key = strdup(line_readed); 复制到新的内存位置,并将新地址填入line_readed

您还需要初始化

new_node->key

在创建每个节点后,也可以重置char *line_readed = NULL; len = 0;,这几乎完全相同,但避免了字符串副本。

此外:不要忘记您需要显式和手动释放每个已分配的内存块。没有垃圾收集。这包括最后的line_readed = NULL;,释放每个节点和每个节点的密钥(如果已分配)。