我是C的新手,并且已经学习了K& R的书“C编程语言”。 在对二叉树进行练习之后,我想为二叉树制作一个标题 char *,long和double。
下面的代码中有一个函数让我感到悲伤 - 它应该填充一个字符指针数组,其中的值以字典顺序存储在树中,但它在某处有错误。这是String Tree Header btree.h的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/************** TYPES **************/
typedef struct ctree
{
char *name;
ctree *left;
ctree *right;
};
/************** Globals **************/
static int c_inc = 0;
/************** Function Prototypes **************/
ctree *add_to_c_tree (ctree *cnode, char *name);
void print_c_tree (ctree *cnode);
ctree *c_tree_alloc (void);
void c_tree_free (ctree *cnode);
void return_c_tree (ctree *cnode, char **array);
/************** Function Definitions **************/
/* add_to_c_tree() : Adds a new node to a *character binary tree */
ctree *add_to_c_tree (ctree *cnode, char *name){
/* If the node is null, allocate memory for it,
* copy the name and set the internal nodes to null*/
if(cnode == NULL){
cnode = c_tree_alloc();
cnode->name = strdup(name);
cnode->left = cnode->right = NULL;
}
/* If initialised then add to the left node if it is lexographically
* less that the node above it else add it to the right node */
else{
if(strcmp(name, cnode->name) < 0)
cnode->left = add_to_c_tree(cnode->left,name);
else if(strcmp(name, cnode->name) > 0)
cnode->right = add_to_c_tree(cnode->right,name);
}
return cnode;
}
/* print_c_tree() : Print out binary tree */
void print_c_tree(ctree *cnode){
if (cnode != NULL) {
print_c_tree(cnode->left);
printf("%s\n",cnode->name);
print_c_tree(cnode->right);
}
}
/* return_c_tree() : return array of strings containing all values in binary tree */
void return_c_tree (ctree *cnode, char **array){
if (cnode != NULL) {
return_c_tree (cnode->left,array+c_inc);
c_tree_free(cnode->left);
*(array+c_inc++) = strdup(cnode->name);
// printf("arr+%d:%s\n", c_inc-1,*(array+(c_inc-1)));
return_c_tree (cnode->right,array+c_inc);
c_tree_free(cnode->right);
}
}
/* c_tree_alloc() : Allocates space for a tree node */
ctree *c_tree_alloc(void){
return (ctree *) malloc(sizeof(ctree));
}
/* c_tree_free() : Free's Memory */
void c_tree_free (ctree *cnode){
free(cnode);
}
我用bt.c进行了测试:
#include "btree.h"
int main(void){
ctree *node = NULL; char *arr[100];
node = add_to_c_tree(node, "foo");
node = add_to_c_tree(node, "yoo");
node = add_to_c_tree(node, "doo");
node = add_to_c_tree(node, "woo");
node = add_to_c_tree(node, "aoo");
node = add_to_c_tree(node, "boo");
node = add_to_c_tree(node, "coo");
print_c_tree(node);
return_c_tree(node,arr);
for (int i = 0; i < 7; ++i)
{
printf("%d:%s ..\n",i, arr[i]);
}
return 0;
}
这个问题的原因是我遇到了return_c_tree()函数的问题,该函数用于模仿K&amp; R的print_c_tree()函数的行为,除了代替递归调用自身直到NULL ptr并打印出来它是按字典顺序排列的节点名称,用于将其名称添加到字符ptrs数组中并释放节点内存。
然而,我在上面运行时得到的输出是:
aoo
boo
coo
doo
foo
woo
yoo
0:aoo ..
1:(null) ..
2:boo ..
3:doo ..
4:foo ..
5:coo ..
6:(null) ..
这表明打印功能正常但返回功能显然不是。 令人困惑的是,如果取消注释return_c_tree()中对printf()的调用,结果是:
aoo
boo
coo
doo
foo
woo
yoo
arr+0:aoo
arr+1:boo
arr+2:coo
arr+3:doo
arr+4:foo
arr+5:woo
arr+6:yoo
0:aoo ..
1:(null) ..
2:boo ..
3:doo ..
4:foo ..
5:coo ..
6:(null) ..
这意味着它确实以正确的顺序添加字符串。 我也试过没有c_inc变量 - &gt;即只增加数组 在将它传递给右边节点之前,该节点从printf产生相同的结果 在return_c_tree()中但与main不同:
arr+-1:aoo
arr+-1:boo
arr+-1:coo
arr+-1:doo
arr+-1:foo
arr+-1:woo
arr+-1:yoo
0:foo ..
1:yoo ..
2:coo ..
3:(null) ..
4:(null) ..
5:(null) ..
6:(null) ..
我很困惑,所以如果有人能提供帮助,我会非常感激。我确定我只是在错误的地方增加它但我无法解决问题。
我以为我终于理解了指针,但显然没有。
最佳P
答案 0 :(得分:5)
您的问题是在递归调用时如何处理指向array
的指针。这将解决您的return_c_tree
功能:
void return_c_tree (ctree *cnode, char **array)
{
if (cnode != NULL) {
return_c_tree (cnode->left,array); // <--- CHANGED 2ND PARAM
c_tree_free(cnode->left);
*(array+c_inc++) = strdup(cnode->name);
return_c_tree (cnode->right,array); // <--- AGAIN, CHANGED 2ND PARAM
c_tree_free(cnode->right);
}
}
您正在使用全局变量c_inc
来跟踪数组中的当前索引。但是,当您递归调用return_c_tree
时,您传递了array+c_inc
,但是您没有将c_inc偏移以解释此问题。基本上,您每次都会重复计算c_inc
。
虽然这可以解决您的特定问题,但您的代码还存在其他一些问题。
通常,使用全局变量会遇到麻烦。这里没有必要这样做。将c_inc
作为参数传递给return_c_tree
。
此外,将全局变量与递归混合特别容易出问题。你真的想要递归例程来保持它们的状态。
正如评论者指出的那样,btree.h
中的所有代码都应该在btree.c
中。头文件的要点是定义一个接口,而不是代码。
(这更具风格性)你的return_c_tree
函数实际上是两个不同的函数:将树的元素(按顺序)复制到数组中,释放内存树使用的。这两个操作在概念上是截然不同的:有时你会想要做一个而不是两个。可能有令人信服的表现(或其他)理由将两者混合在一起,但要等到你有一些确凿的证据。