简体   繁体   English

何时将指针传递给结构作为参数,以及何时将指针传递给结构的指针?

[英]When to pass a pointer to a structure as an argument, and when to pass a pointer to a pointer to a structure?

My question is in regards to the following code. 我的问题是关于以下代码。

#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);
    }
}

So, the code works perfectly as is. 因此,代码按原样完美运行。 It will insert each value into BST correctly, and the traversal function will traverse the tree inorder correctly. 它将正确地将每个值插入BST,并且遍历函数将正确地遍历树。 However, when I am initially declaring t to be a BST (eg line 27), if I also assign t to be NULL (eg BST * t = NULL), then the insertion does not work anymore. 但是,当我最初声明t为BST时(例如,第27行),如果我也将t分配为NULL(例如BST * t = NULL),则插入不再起作用。 But, if I then reassign t for the first insertion (eg t = bst_insert(t, 5)), then everything works again. 但是,如果我随后为第一次插入重新分配了t(例如t = bst_insert(t,5)),那么一切都会再次起作用。 Is there a particular reason for this? 是否有特定原因?

Secondly, how do I know when I need to pass a pointer to a pointer to a structure? 其次,如何知道何时需要将指针传递给结构的指针? If I want to change the value that int i points to, then I need to pass &i to a function, correct? 如果我想更改int i指向的值,那么我需要将&i传递给函数,对吗? But if I want to change the values within struct node n , then why do I need to pass a **node to a function, and not just a *node ? 但是,如果要更改struct node n的值,那么为什么需要将**node传递给函数,而不仅是*node

Thank you so much for taking a look. 非常感谢您的关注。

In C, everything is passed by value, there is no exception to this. 在C语言中, 一切都是通过值传递的,对此也不例外。

You can emulate pass-by-reference by passing the pointer and dereferencing it in the function but this is a poor cousin to real pass-by-reference. 您可以通过传递指针并在函数中取消对指针的引用来模拟传递引用,但这与真实的传递引用不太理想。

The bottom line is, if you want to change anything passed to a function, you have to provide its pointer for dereferencing and, for changing pointers themselves, that means passing the pointer of the pointer. 最重要的是,如果您想更改传递给函数的任何内容 ,则必须提供其指针以供取消引用,而要更改指针本身,则意味着要传递指针的指针。 Note that: 注意:

t = modifyAndReturn (t);

is not really the same thing - the function itself does not modify t , it simply returns something which the caller then assigns to t . 并不是完全一样-函数本身不会修改t ,它只返回调用者然后分配给t

So, you've done it the latter way, where you can do something like this: 因此,您采用了后一种方法,可以执行以下操作:

int add42 (int n) { return n + 42; }
:
x = add42 (x);

Using the emulated pass-by-reference, that would be (with pointers and dereferencing): 使用模拟的传递引用,将是(带有指针和取消引用):

void add42 (int *n) { *n += 42; }
:
add42 (&x);

For changing pointers, as mentioned earlier, you need to pass the pointer to the pointer. 如前所述,要更改指针,您需要将指针传递给指针。 Let's say you want to change a char pointer so that it points to the next character. 假设您要更改一个char指针,使其指向下一个字符。 You would do something like: 您将执行以下操作:

#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++ actually provides proper pass-by-reference with the & "modifier" such as: C ++实际上使用& “修饰符”提供正确的按引用传递,例如:

void add42 (int &n) { n += 42; }

and you don't have to then worry about derefencing within the function, any changes are immediately echoed back to the original passed parameter. 而且您不必担心在函数中进行反引用,任何更改都会立即回显到原始传递的参数中。 I rather hope that C21 will have this feature, it will save a lot of trouble for people unfamiliar with the pointer gymnastics we have to endure in C :-) 我希望C21具有此功能,它将为不熟悉我们必须在C中忍受的指针体操的人们节省很多麻烦:-)


By the way, you have a rather serious problem with that code. 顺便说一句,您对该代码有一个相当严重的问题。 Within main , the line: main ,该行:

BST * t;

will set t to an arbitrary value which is unlikely to be what you want. t设置为不太可能是您想要的任意值。 You should set it initially to NULL so that bst_insert correctly initialises it. 应该首先将其设置为NULL,以便bst_insert正确初始化它。

Actually your code is incorrect as it is, because you are not allocating memory for t in your main. 实际上,您的代码实际上是不正确的,因为您没有在main中为t分配内存。 You will get undefined behavior when you try to access the value pointed in bst_insert_node . 当您尝试访问bst_insert_node指向的值时,您将获得未定义的行为。 The correct way to do it would be to set it to NULL and then 正确的方法是将其设置为NULL ,然后

t = bst_insert(t, 5);  

BST *t; // It declares a pointer of type BST
BST *t = NULL; // Declares pointer same way and pointer points to mem location "0"

Now, 现在,

bst_insert(t, 5); // Now "tree" pointer inside "bst_insert" definition points to location pointed by "t" ie NULL (in 2nd case above)

tree = malloc(<some memory>); // Allocates some memory and stores its base address inside "tree"

Now see, above statement changes the location pointed by tree in bst_insert() , but not by t in main() . 现在来看,上面的语句更改了bst_insert() tree指向的位置,而main() t指向的位置未更改。

So when you write t = bst_insert(t, 5) you return the value pointed by tree explicitly to store in tree, So code works fine. 因此,当您编写t = bst_insert(t, 5) ,将显式地返回树所指向的值以存储在树中,因此代码可以正常工作。

If you do not use the return value, t is still NULL . 如果不使用返回值,则t仍为NULL

Either use this way, or you can do like this: 可以使用这种方式,也可以这样:

bst_insert(BST **tree, int new_val);

And in main() main()

bst_insert(&t, 5);

What happens now!! 现在会发生什么!

Now you will use *tree = malloc(<some mem>); 现在,您将使用*tree = malloc(<some mem>);

So you directly assign base address of memory allocated to: *tree which is actually t itself. 因此,您可以直接分配分配给以下对象的内存的基地址: *tree ,它实际上是t本身。 Now you need not return the mem address. 现在,您无需返回内存地址。

This is same as code below, written inside one function: 这与下面的代码相同,写在一个函数中:

BST **tree;
BST *t;

tree = &t;
*tree = malloc(<some mem>);  // You are allocating memory and storing address
                             // actually at "t"

Hope you understand what I want to explain. 希望您理解我要解释的内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM