1

私の質問は、次のコードに関するものです。

#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

ご覧いただきありがとうございます。

4

3 に答える 3

5

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++ は実際には、次のような「修飾子」を使用して適切な参照渡しを提供します。&

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

関数内での参照解除について心配する必要はありません。変更はすぐに元の渡されたパラメーターにエコーバックされます。私はむしろ、C21 がこの機能を備えていることを願っています。C で耐えなければならないポインター体操に慣れていない人にとっては、多くの問題を解決してくれるでしょう :-)


ところで、そのコードにはかなり深刻な問題があります。内mainの行:

BST * t;

tあなたが望むものである可能性が低い任意の値に設定されます。正しく初期化されるように、最初は NULL に設定する必要があります。bst_insert

于 2013-08-30T04:32:00.943 に答える
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"

上記のステートメントはtreeinが指す位置を変更しますが、 inは変更しbst_insert()ません。tmain()

したがって、書くとき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それ自体です。これで、mem アドレスを返す必要がなくなりました。

これは、1 つの関数内に記述された以下のコードと同じです。

BST **tree;
BST *t;

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

私が説明したいことを理解していただければ幸いです。

于 2013-08-30T04:45:08.407 に答える