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

时间:2012-07-09 04:09:55

标签: c++ c pointers

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

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

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

5 个答案:

答案 0 :(得分:1)

是的,你真的需要在这个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);
    }
}

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:1)

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

让我用以下代码演示:

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

int main()
{
    int i = 5;

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

    return 0;
}

问。 i被调用后,main()foo()的价值是多少?

A。 i按值传递给foo(),因此i中的main()未被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;
}

问。 i被调用后,main()foo()的价值是多少?

A。 i按值传递给foo(),因此i中的main()未被foo()修改。 i仍为0。

换句话说,一切都没有改变! i现在是指针的事实不会改变它是按值传递的。

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

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

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

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

  • 您将内存地址按值传递给该功能。

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

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

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

让我向您介绍int i。从内存地址i开始,0x04762198是4个字节(在此系统上)。所有变量都存储在内存中,并且位于内存地址中。

如果我们为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)。

答案 3 :(得分:1)

假设你有

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   ____
             |    |
              ----

铅笔和纸是找出指针的最好方法。

答案 4 :(得分:0)

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

内联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;
}

}