我在一个程序中使用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
答案 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%明确。