【问题标题】:When to pass a pointer to a structure as an argument, and when to pass a pointer to a pointer to a structure?何时将指向结构的指针作为参数传递,何时将指向结构的指针传递?
【发布时间】:2013-09-02 16:59:44
【问题描述】:

我的问题是关于以下代码。

#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 指向的值,那么我需要将&amp;i 传递给一个函数,对吗?但是如果我想更改struct node n 中的值,那么为什么我需要将**node 传递给函数,而不仅仅是*node

非常感谢您的观看。

【问题讨论】:

    标签: c pointers tree arguments


    【解决方案1】:

    在C语言中,一切都是按值传递的,这个也不例外。

    您可以模拟传递指针并在函数中取消引用它,但这是真正的传递引用的糟糕表亲。

    底线是,如果您想更改传递给函数的任何内容,您必须提供其指针以进行解引用,并且为了更改指针本身,这意味着传递指针的指针。请注意:

    t = modifyAndReturn (t);
    

    并不是一回事——函数本身不会修改t,它只是返回调用者然后分配给t的东西。

    所以,你已经完成了后一种方式,你可以这样做:

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

    使用模拟的传递引用,那将是(使用指针和取消引用):

    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++ 实际上提供了 proper 通过引用传递 &amp; “修饰符”,例如:

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

    而且您不必担心函数内的取消引用,任何更改都会立即回显到原始传递的参数。我倒是希望C21能有这个功能,对于不熟悉C语言中我们不得不忍受的指针体操的人来说,它会省去很多麻烦:-)


    顺便说一句,你的代码有一个相当严重的问题。在main 内,行:

    BST * t;
    

    会将t 设置为一个不太可能是您想要的任意值。您应该将其初始设置为 NULL,以便 bst_insert 正确初始化它。

    【讨论】:

    • 很抱歉有这么多问题,但还有一件事——在我将 t 设置为 NULL 后,bst_insert 不会修改 t(例如,当我 traverseInOrder t 时,没有任何反应)。但是,如果我将 t 分配给第一个插入函数(例如 t = bst_insert(t, 5)),但不分配给其余的插入函数,即使未重新分配 t 的插入函数仍会修改 t。为什么会发生这种情况?
    • @dyxh :遍历最初只需要根地址,其余地址由左右指向。因此,您不应重新分配 t 以返回每个插入的值。
    • @dyxh,这可能是另一个问题的最佳选择。这样,你会得到更多的回应。如果您在评论框中提出问题,那么很少有人会看到它。
    【解决方案2】:

    实际上,您的代码不正确,因为您没有在 main.xml 中为 t 分配内存。当您尝试访问bst_insert_node 中指向的值时,您将获得未定义的行为。 正确的做法是将其设置为NULL,然后

    t = bst_insert(t, 5);  
    

    【讨论】:

      【解决方案3】:

      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(&lt;some memory&gt;); // Allocates some memory and stores its base address inside "tree"

      现在看,上面的语句改变了bst_insert()中的tree指向的位置,而不是main()中的t

      所以当你写 t = bst_insert(t, 5) 时,你会显式返回 tree 指向的值以存储在树中,所以代码可以正常工作。

      如果不使用返回值,t 仍然是NULL

      要么使用这种方式,要么你可以这样做:

      bst_insert(BST **tree, int new_val);

      main()

      bst_insert(&amp;t, 5);

      现在发生了什么!

      现在你将使用*tree = malloc(&lt;some mem&gt;);

      所以你直接分配内存的基地址分配给:*tree,实际上是t 本身。现在你不需要返回mem地址了。

      这与下面的代码相同,写在一个函数中:

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

      希望你明白我要解释的内容。

      【讨论】:

        猜你喜欢
        • 2013-11-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-15
        • 1970-01-01
        • 2021-07-17
        • 2012-04-04
        • 1970-01-01
        相关资源
        最近更新 更多