我的问题是关于以下代码。
#include <stdio.h>
#include <stdlib.h>
struct node
{
int v;
struct node * left;
struct node * right;
};
typedef struct node Node;
struct bst
{
Node * root;
};
typedef struct bst BST;
BST * bst_insert(BST * tree, int newValue);
Node * bst_insert_node(Node * node, int newValue);
void bst_traverseInOrder(BST * tree);
void bst_traverseInOrderNode(Node * node);
int main(void)
{
BST * t;
bst_insert(t, 5);
bst_insert(t, 8);
bst_insert(t, 6);
bst_insert(t, 3);
bst_insert(t, 12);
bst_traverseInOrder(t);
return 0;
}
BST * bst_insert(BST * tree, int newValue)
{
if (tree == NULL)
{
tree = (BST *) malloc(sizeof(BST));
tree->root = (Node *) malloc(sizeof(Node));
tree->root->v = newValue;
tree->root->left = NULL;
tree->root->right = NULL;
return tree;
}
tree->root = bst_insert_node(tree->root, newValue);
return tree;
}
Node * bst_insert_node(Node * node, int newValue)
{
if (node == NULL)
{
Node * new = (Node *) malloc(sizeof(Node));
new->v = newValue;
new->left = NULL;
new->right = NULL;
return new;
}
else if (newValue < node->v)
node->left = bst_insert_node(node->left, newValue);
else
node->right = bst_insert_node(node->right, newValue);
return node;
}
void bst_traverseInOrder(BST * tree)
{
if (tree == NULL)
return;
else
{
bst_traverseInOrderNode(tree->root);
printf("\n");
}
}
void bst_traverseInOrderNode(Node * node)
{
if (node == NULL)
return;
else
{
bst_traverseInOrderNode(node->left);
printf("%d ", node->v);
bst_traverseInOrderNode(node->right);
}
}
因此,代码完美无瑕。它会正确地将每个值插入到BST中,并且遍历函数将正确地遍历树。但是,当我最初声明t为BST(例如第27行)时,如果我还将t指定为NULL(例如BST * t = NULL),则插入不再起作用。但是,如果我然后为第一次插入重新分配t(例如t = bst_insert(t,5)),那么一切都会再次起作用。这有什么特别的原因吗?
其次,我如何知道何时需要将指针传递给指向结构的指针?如果我想更改int i
指向的值,那么我需要将&i
传递给函数,对吗?但是,如果我想更改struct node n
中的值,那么为什么我需要将**node
传递给函数,而不仅仅是*node
?
非常感谢你看一看。
答案 0 :(得分:5)
在C中,所有都是按值传递的,对此没有例外。
您可以通过传递指针并在函数中取消引用它来模拟传递引用,但这是真正的传递引用的表兄弟。
底线是,如果你想改变传递给函数的任何,你必须提供它的指针用于解除引用,并且,为了更改指针本身,这意味着传递指针的指针。请注意:
t = modifyAndReturn (t);
实际上不是一回事 - 函数本身不会修改t
,它只会返回调用者然后分配给t
的内容。
所以,你已经用后一种方式完成了,你可以做这样的事情:
int add42 (int n) { return n + 42; }
:
x = add42 (x);
使用模拟的pass-by-reference,即(使用指针和解除引用):
void add42 (int *n) { *n += 42; }
:
add42 (&x);
如前所述,要更改指针,需要将指针传递给指针。假设你要更改一个char指针,使其指向下一个字符。你可以这样做:
#include <stdio.h>
void pointToNextChar (char **pChPtr) {
*pChPtr += 1; // advance the pointer being pointed to.
}
int main (void) {
char plugh[] = "hello";
char *xyzzy = plugh;
pointToNextChar (&xyzzy);
puts (xyzzy); // outputs "ello".
}
C ++实际上使用&
“修饰符”提供正确的传递引用“,例如:
void add42 (int &n) { n += 42; }
并且您不必担心函数内的derefencing,任何更改都会立即回显到原始传递的参数。我更希望C21能有这个功能,对于不熟悉我们必须忍受的C指针体操的人来说会省去很多麻烦: - )
顺便说一句,你对这段代码有一个相当严重的问题。在main
内,行:
BST * t;
会将t
设置为一个不太可能符合您要求的任意值。您应将其初始设置为NULL,以便bst_insert
正确初始化它。
答案 1 :(得分:1)
实际上你的代码不正确,因为你没有为你的主要内容t
分配内存。当您尝试访问bst_insert_node
中指向的值时,您将获得未定义的行为。
正确的方法是将其设置为NULL
然后
t = bst_insert(t, 5);
答案 2 :(得分:0)
BST *t; // It declares a pointer of type BST
BST *t = NULL; // Declares pointer same way and pointer points to mem location "0"
现在,
bst_insert(t, 5); // Now "tree" pointer inside "bst_insert" definition points to location pointed by "t" i.e. NULL (in 2nd case above)
tree = malloc(<some memory>); // Allocates some memory and stores its base address inside "tree"
现在请注意,上面的语句更改tree
中bst_insert()
指向的位置,而不是t
中main()
指向的位置。
因此,当您编写t = bst_insert(t, 5)
时,您将显式返回树指向的值以存储在树中,因此代码可以正常工作。
如果您不使用返回值,t
仍为NULL
。
使用这种方式,或者你可以这样做:
bst_insert(BST **tree, int new_val);
并在main()
bst_insert(&t, 5);
现在发生了什么!!
现在您将使用*tree = malloc(<some mem>);
因此,您直接将分配的内存的基址分配给:*tree
,实际上t
本身。现在您无需返回内存地址。
这与下面的代码相同,写在一个函数中:
BST **tree;
BST *t;
tree = &t;
*tree = malloc(<some mem>); // You are allocating memory and storing address
// actually at "t"
希望你明白我想要解释的内容。