Realloc搞乱了tsearch创建的树

时间:2014-02-19 22:02:04

标签: c binary-search-tree realloc

我在一个程序中使用search.h库中的tsearch / tfind / twalk函数,该程序基本上对文档中的唯一单词进行排序和计数。因为我不知道文档中有多少单词,所以我使用malloc为数组分配一定量的内存,最后保留所有单词,然后使用realloc使其更大,如果我已经填满了。但是,realloc显然会破坏由tsearch维护的树,并且twalk开始返回垃圾节点或者节点内容正在被破坏。

结构定义:

struct word {
    char word[MAX_MTEXT];
    int occur;
};

struct mymsg {
    long mtype;
    char mtext[MAX_MTEXT];
    int occur;
};

下面的代码是整个子进程代码,还有其他一些处理从消息队列中获取单词的东西:

f = 1;
i = 0;
words_entered = 0;
entry = (struct word *) malloc(words_allocated * sizeof(struct word));
while(f) {
    if (msgrcv(m_key, &mymsg, sizeof(struct mymsg), (long) getpid(), 0) == -1) {
        perror("recieve");
        exit(EXIT_FAILURE);
    }
    //printf("%s recieved\n",mymsg.mtext);
    if (mymsg.mtext[0] == '\0') {
        //printf("term recv\n");
        f = 0;
    }
    else {
            //printf("mtext = %s\n",mymsg.mtext);
        memcpy(&entry[i].word,&mymsg.mtext,MAX_MTEXT);
        //printf("entry = %s\n",entry[i].word);
        entry[i].occur = 1;
        //printf("%s entered\n",entry[i].word);
        words_entered++;
        if (words_entered == words_allocated) {
            printf("About to realloc\n\n");
            twalk (root, action);
            words_allocated = words_allocated *2;
            entry = (struct word *) realloc(entry,(size_t) words_allocated * sizeof(struct word));
            printf("After realloc\n\n");
            twalk (root, action);
        }
        ptr = tfind(&entry[i],&root,compare);
        if (ptr == NULL) {
            //printf("null\n");
            ptr = tsearch(&entry[i],&root,compare);
            //printf("%s added to tree\n",(*ptr)->word);
        }
        else {
            (*ptr)->occur++;
        }
        i++;
        //printf("check\n");
    }
}
twalk (root, action);
mymsg.mtype = ret_id;
mymsg.mtext[0] = '\0';
mymsg.occur = 0;
if (msgsnd(m_key, &mymsg, sizeof(mymsg)-sizeof(long), 0) == -1) {
    perror("send");
    exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);

这是由walk:

调用的动作代码
void action(const void *nodep, VISIT value, int level) {
    struct word *w = *((struct word **) nodep);
    struct mymsg mymsg;
    switch (value) {
    case leaf:
    case postorder:
        printf("%s: %i, level %i\n",w->word, w->occur, level);
        mymsg.mtype = ret_id;
        strcpy(mymsg.mtext,w->word);
        //printf("%s vs %s\n",w->word,mymsg.mtext);
        mymsg.occur = w->occur;
        if (msgsnd(m_key, &mymsg, sizeof(mymsg)-sizeof(long), 0) == -1) {
            perror("send");
            exit(EXIT_FAILURE);
        }
        break;
    default:
        break;
    }
    return;
}

这是运行初始分配5的结果:

About to realloc

each: 1, level 1
is: 1, level 0
therefore: 1, level 2
translator: 1, level 1

After realloc

Ð3³: 1, level 1
is: 1, level 0
therefore: 1, level 2
translator: 1, level 1

About to realloc

for: 1, level 2
his: 1, level 1
$p  : 158343352, level 2
is: 1, level 0
own: 1, level 3
portion;: 1, level 2
responsible: 1, level 3
therefore: 1, level 1
p p rlator: 1, level 2

After realloc

for: 1, level 2
his: 1, level 1
$p  : 158343352, level 2
is: 1, level 0
own: 1, level 3
portion;: 1, level 2
responsible: 1, level 3
therefore: 1, level 1
p p rlator: 1, level 2

1 个答案:

答案 0 :(得分:1)

免责声明:我之前从未使用树搜索功能使用过GNU C.

现在,如果我查看相应的文档:

- 功能:void * tsearch(const void * key,void ** rootp,comparison_fn_t compar)

如果树不包含匹配的条目,键值将添加到树。 tsearch对键指向的对象没有复制(由于大小未知,它怎么可能)。相反,向此对象添加引用,这意味着只要使用树数据结构,对象就必须可用。

每次realloc需要移动内存时,您的树节点指针都会失效。此外,您不希望tsearch返回NULL。

最简单的解决方案是分配单个单词元素,而不是在数组中缓冲它们。这可能会带来一些速度惩罚。

如果您确实需要将单词条目排列在块中,那么为什么不只是将root更新并更新所有元素指针如果realloc(entry,...)!= entry?编辑:根据描述,您可能会遇到UB。但是,如果他们谈论一般案件或MT案件,则不是100%明确。