繁体   English   中英

为什么使用本地指针不起作用

[英]why using a local pointer wouldn't work

inline void insert(node *root, int value)
{
    if(!root)
    {
        root = new node();
        root->value = value;
    }
    else
    {
        node *itr = root;
        while(1)
        {
            if(itr->value > value)
                itr = itr->left;
            else
                itr = itr->right;
            if(!itr)
            {
                itr = new node();
                itr->value = value; 

                break;
            }

        }
    }
}

//像这样调用插入函数

node *head = 0;
insert(head, 5);
insert(head, 10);
insert(head, 3);
insert(head, 1);
insert(head, 4);

我知道这段代码不起作用,因为插入函数中的'itr'是局部变量,因此,它不会反映方法之外的树。 但是,我不清楚为什么它不起作用。 虽然'itr'是局部变量,'itr'指向'root'指向的相同位置。 此外,我取消引用它来移动'左'或'右'所以我认为它应该工作。

我认为这是通过值与指针传递指针的基本问题,但我无法找到为什么我不能使用指针的局部变量更改树的明确解释。

是的,你真的需要在这个c ++代码中使用引用。

node * treeRoot = 0;
insert( treeRoot , 4 );  // treeRoot needs to be changed for this to work. So a reference is required. 

在这种情况下,该函数应声明为

void insert(node* &root, int value);

另一种方法是使用双指针。

node * treeRoot = 0;
insert( &treeRoot , 4 );  // treeRoot needs to be changed for this to work. So a pointer to the pointer will work. 

在这种情况下,该函数应声明为

void insert(node** root, int value);

这也是探索紧凑而优雅的递归解决方案的绝佳机会。 (虽然内联在这里不起作用)

void insert(node* &root, int value)
{
    if(!root)
    {
        root = new node();
        root->value = value;
    }
    else
    {
        if(root->value > value)
            insert( root->left, value );
        else
            insert( root->right, value);
    }
}

这里有两个问题。 首先,在您的示例用法中,您有:

node *head = 0;

如果你运行它,你将最终在每次调用时创建一个新节点,因为

if (!root) {}

永远都是真的。 其次,正如您所指出的,当您更新树时,您正在创建一个新节点,但实际上并未将其插入树中。 要更正这两个问题,您可以执行以下操作:

node* insert(node *root, int value) {
    if(!root) {
        root = new node();
        root->value = value;
    } else {
        node *itr = root;
        node **where = 0;
        while(1) {
            if(itr->value > value) {
                where = &itr->left;
                itr = itr->left;
            } else {
                where = &itr->right;
                itr = itr->right;
            }
            if(!itr) {
                itr = new node();
                itr->value = value; 
                // Insert the new node, to fix the second issue
                *where = itr;
                break;
            }
            prev = itr;    
        }
    }
    return root; // This always returns the tree, solves problem 1, which can also be solved using references or ptr-to-ptr as other people have mentioned
}

然后你的例子是:

node* head = insert(0, 5);
insert(head, 10);
insert(head, 3);
insert(head, 1);
insert(head, 4);

虽然仍然存在两次插入相同数字的问题,但这里处理不当。 为了测试,避免在这样的函数中创建新节点是一种更好的做法。 最好创建节点然后为节点提供要插入的新值,并让插入数据将其链接到树(包括头部)的位置。

如果以正确的方式思考它们,C(和C ++)中的指针并不是那么困难。

让我用以下代码演示:

void foo(int i) {
    i = i + 5;
}

int main()
{
    int i = 5;

    foo(i);
    printf("i is %d\n", i);

    return 0;
}

问:在调用foo()之后, main()i的值是多少?

A. i通过值传递给foo() ,因此main()i不会被foo()修改。 i还是5岁。

现在,让我们稍微改变一下代码:

void foo(int* i) {
    i = malloc(sizeof(int));
}

int main()
{
    int *i = 0;

    foo(i);
    printf("i is %p\n", i); /* printf a pointer with %p */

    return 0;
}

问:在调用foo()之后, main()i的值是多少?

A. i通过值传递给foo() ,因此main()i不会被foo()修改。 i还是0。

换句话说,一切都没有改变! i现在是一个指针的事实并没有改变它是通过值传递的。

实际上,在C中,所有函数参数都是按值计算的。 那么,我们如何获得修改变量的函数呢?

如果要将变量传递给该函数的函数以对其进行修改,则必须将该变量的地址传递给该函数。 (对于C来说也是如此。在C ++中你也可以使用引用,但我只是在这里谈论指针。)

传递变量的地址时,您正在做两件事:

  • 您正在计算变量的内存地址

  • 您将该内存地址按值传递给该函数。

存储器地址可用于修改存储器地址所指向的存储器。 由于函数内部的内存地址与函数调用之外的内存地址相同(因为它是通过值传递的),因此它们指向的变量是相同的!

这真是最棘手的概念,所以让我们画一些ascii。

|            |
+------------+ <- 0x04762198
|            |
|     i      |
|            |
|            |
+------------+ <- 0x0476219C
|            |

我来介绍一下int i i是4个字节(在这个系统上)从内存地址0x04762198 所有变量都存储在内存中的某个位置,并且位于内存地址中。

如果我们为i赋值,则该值将存储在上面的内存块中。

如果我们将i传递给函数,则i的值将被复制到内存中的其他位置以供函数使用。 该内存的值将与我们的原始i相同,但该变量的内存地址将位于其他位置。

这是巧妙的一点。 如果我们将0x04762198传递给函数,那么该函数现在可以访问原始i的内存位置! 这是一个指针,因为它指向内存中的地址而被调用。 如果我们想使用指针修改函数内部的原始i ,我们取消引用它(例如。 *ptr = 5; )。 我们实际在做的是说“请将此值(5)存储在ptr指向的内存中”

让我们再次更改代码来实现这个:

/*
 * The address of an int* is int**
 */
void foo(int** i) {
    /* dereference to access and modify the original `i` */
    *i = malloc(sizeof(int));
}

int main()
{
    int *i = 0;

    /*
     * Pass the address of i, so foo() can modify i
     */    
    foo(&i);
    printf("i is %p\n", i); /* printf a pointer with %p */

    return 0;
}

看到不同?

现在,您能否在自己的计划中看到自己做错了什么?

注意:为了简洁,我省略了通常的错误检查(例如,检查malloc()不返回NULL)。

假设你有

int x = 0;
int y = x;
y = 3478;

你期望x还包含3478吗?
我知道你不会,你的rootitr也是如此。

这是一个经典的铅笔纸问题(大多数指针问题都是),当你遇到这样的问题时,绝对值得拔出一些死树。

这是你的一个案例的ASCII版本,你要插入到右边,右边是NULL。
箭头显示各个变量指向的位置。

功能开始:

           ____
root ---> |    |
          ------
           / \
          /   \
       left   NULL

itr = root;    
           ____
root ---> |    | <--- itr
          ------
           / \
          /   \
       left   NULL

itr = itr->right;
           ____
root ---> |    | 
          ------
           / \
          /   \
       left   NULL <--- itr

if (!itr)
    itr = new node();

           ____
root ---> |    | 
          ------
           / \
          /   \                   ____
       left   NULL      itr ---> |    |
                                  ----

如您所见,输入树根本没有被修改,您只是在其外部分配了一个新节点并将其保留在那里。

这可行:

           ____
root ---> |    | <--- itr
          ------
           / \
          /   \
       left   NULL

   if (!itr->right)
   {
       itr->right = new node()
   }


           ____
root ---> |    | <--- itr
          ------
           / \
          /   \
       left   ____
             |    |
              ----

铅笔和纸是弄清楚指针的最佳方法。

你创建了一个节点,但没有将它插入树中。 试试这段代码:

内联void插入(node * root,int value)

{

if(!root)

{
    root = new node();
    root->value = value;
}
else
{
    node *itr = root , *prev;
    while(itr)
    {
        prev=itr;
        if(itr->value > value)
            itr = itr->left;

        else
            itr = itr->right;
    }

        itr = new node();
        itr->value = value;

        if(value < prev->value)
        prev->left=itr;
        else
        prev->right=itr;
}

}

暂无
暂无

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

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