![](/img/trans.png)
[英]Why wouldn't qCritical or qDebug work when called from a destructor?
[英]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
吗?
我知道你不会,你的root
和itr
也是如此。
这是一个经典的铅笔纸问题(大多数指针问题都是),当你遇到这样的问题时,绝对值得拔出一些死树。
这是你的一个案例的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.