澄清 C/C++ 中的悬挂指针
Clarify dangling pointer in C/C++
我对 C/C++
中的悬挂指针有点困惑
void remove(){
Node* curr = new Node(10);
Node* pt = curr;
delete curr;
// do something here
// do other thing here
}
我假设 Node* pt
在函数 remove()
终止之前仍然是悬挂指针?
remove()
结束后指针Node* pt
不用担心了?
你的问题似乎是基于一个错误的假设,即悬挂指针有问题。它绝对没有错,尝试将所有悬挂指针设置为 NULL
等是一种反模式。只是不要取消引用悬空指针。
在 remove
的注释部分,但是 curr
和 pt
是悬垂指针,因为它们都指向已删除的对象。只要该代码不取消引用它们,就没有问题。
当您调用 delete curr
时,存储在 curr
中的值没有改变,但该位置的内存已 return 写入系统。
让我们逐行查看您的代码:
Node* curr = new Node(10);
为了论证,假设分配的内存地址为 0x1000,这意味着 curr
现在的值为 0x1000
。
Node* pt = curr;
pt 现在也有值(指向位置)0x1000。
delete curr;
这return将位置 0x1000 处的内存提供给系统。但是,此时 curr
和 pt
都仍然包含值 0x1000。
它们都是悬挂指针。
当你使用裸指针时,悬空指针是不可避免的,而且它们并不是隐含的坏事。您只需要注意不要使用悬挂指针或 return 悬挂指针。
void badfunc() {
char* p = new char[10];
delete p;
return p; // use of dangling pointer
}
关于您的问题:"I assume Node* pt is still tangling pointer before the function remove() is terminated?"
是,在调用delete curr;
之后之前同时被Node* curr
和Node* pt
指向的内存块得到释放。调用 delete curr;
后不应使用 curr
或 pt
值(它们具有相同的值,并且无效)。不要假设对 delete
的调用会更改 curr
的值。它不会 - 您可以通过在调用 delete curr;
之前和之后打印两个值来检查它,如下所示:
printf("%d, %d", curr, pt);
关于您的问题:"I don't have to worry about the pointer Node* pt after remove() is terminated?"
确实,在remove()
完成后Node* curr
和Node* pt
都不复存在。它们也不在 remove()
范围之外(不可访问)。因此您不必担心它们。
另外之前Node* curr
和Node* pt
指向的内存中存储的objects/data被delete curr;
指向freed/destructed,所以你也不用担心。
有时建议通过将它们设置为 NULL 来避免悬挂指针。这并不能缓解问题,但如果您不小心使用了这样的指针,它至少会清楚地体现出来。这是因为尝试取消引用 NULL 指针会导致内存分段错误 - 所以您至少会得到一个 可重现的 运行时错误来查找您的错误。其他人说盲目地将每个未使用的指针设置为 NULL 会使您的代码混乱。这里需要一些智慧来权衡利弊。
悬挂指针是指指向不再有效的对象的指针。这不需要基于 new/delete 或 malloc/free:事实上,非指针可以悬挂。任何对另一个对象或资源的引用,其中引用不再有效,但引用 'does not know that',可以说是 "dangle".
如果你取消引用一个悬空指针,就会发生未定义的行为(有时什么都没有,有时是段错误,有时你的硬盘被格式化,有时你的代码时间旅行(是的,我是认真的))。
所以不要取消引用悬挂指针。
在delete
之后,两个指针都是悬空的。
一般来说,如果您的代码强制要求变量处于您可以从其类型 and/or 名称中确定的已知状态,则有助于对程序进行推理。一个示例可能是 "do not have dangling pointers, set them to null immediately after the delete":那么,如果您总是在创建时将指针初始化为空,则每个指针要么有效要么指向空。
使用持久数据执行此操作是个好主意;在微小的函数中使用局部变量这样做通常会增加更多的噪音而不是帮助。
另一种方法是更喜欢使用智能指针,但它们也有自己的陷阱。引用计数和标记清除智能指针将悬空指针变成资源泄漏!并且 unique ptr 没有 "observer" 安全的指针类型。
在其范围结束后的局部指针不复存在。它不能是悬挂指针,因为它不是指针。
我对 C/C++
中的悬挂指针有点困惑void remove(){
Node* curr = new Node(10);
Node* pt = curr;
delete curr;
// do something here
// do other thing here
}
我假设 Node* pt
在函数 remove()
终止之前仍然是悬挂指针?
remove()
结束后指针Node* pt
不用担心了?
你的问题似乎是基于一个错误的假设,即悬挂指针有问题。它绝对没有错,尝试将所有悬挂指针设置为 NULL
等是一种反模式。只是不要取消引用悬空指针。
在 remove
的注释部分,但是 curr
和 pt
是悬垂指针,因为它们都指向已删除的对象。只要该代码不取消引用它们,就没有问题。
当您调用 delete curr
时,存储在 curr
中的值没有改变,但该位置的内存已 return 写入系统。
让我们逐行查看您的代码:
Node* curr = new Node(10);
为了论证,假设分配的内存地址为 0x1000,这意味着 curr
现在的值为 0x1000
。
Node* pt = curr;
pt 现在也有值(指向位置)0x1000。
delete curr;
这return将位置 0x1000 处的内存提供给系统。但是,此时 curr
和 pt
都仍然包含值 0x1000。
它们都是悬挂指针。
当你使用裸指针时,悬空指针是不可避免的,而且它们并不是隐含的坏事。您只需要注意不要使用悬挂指针或 return 悬挂指针。
void badfunc() {
char* p = new char[10];
delete p;
return p; // use of dangling pointer
}
关于您的问题:"I assume Node* pt is still tangling pointer before the function remove() is terminated?"
是,在调用delete curr;
之后之前同时被Node* curr
和Node* pt
指向的内存块得到释放。调用delete curr;
后不应使用curr
或pt
值(它们具有相同的值,并且无效)。不要假设对delete
的调用会更改curr
的值。它不会 - 您可以通过在调用delete curr;
之前和之后打印两个值来检查它,如下所示:printf("%d, %d", curr, pt);
关于您的问题:"I don't have to worry about the pointer Node* pt after remove() is terminated?"
确实,在remove()
完成后Node* curr
和Node* pt
都不复存在。它们也不在remove()
范围之外(不可访问)。因此您不必担心它们。另外之前
Node* curr
和Node* pt
指向的内存中存储的objects/data被delete curr;
指向freed/destructed,所以你也不用担心。有时建议通过将它们设置为 NULL 来避免悬挂指针。这并不能缓解问题,但如果您不小心使用了这样的指针,它至少会清楚地体现出来。这是因为尝试取消引用 NULL 指针会导致内存分段错误 - 所以您至少会得到一个 可重现的 运行时错误来查找您的错误。其他人说盲目地将每个未使用的指针设置为 NULL 会使您的代码混乱。这里需要一些智慧来权衡利弊。
悬挂指针是指指向不再有效的对象的指针。这不需要基于 new/delete 或 malloc/free:事实上,非指针可以悬挂。任何对另一个对象或资源的引用,其中引用不再有效,但引用 'does not know that',可以说是 "dangle".
如果你取消引用一个悬空指针,就会发生未定义的行为(有时什么都没有,有时是段错误,有时你的硬盘被格式化,有时你的代码时间旅行(是的,我是认真的))。
所以不要取消引用悬挂指针。
在delete
之后,两个指针都是悬空的。
一般来说,如果您的代码强制要求变量处于您可以从其类型 and/or 名称中确定的已知状态,则有助于对程序进行推理。一个示例可能是 "do not have dangling pointers, set them to null immediately after the delete":那么,如果您总是在创建时将指针初始化为空,则每个指针要么有效要么指向空。
使用持久数据执行此操作是个好主意;在微小的函数中使用局部变量这样做通常会增加更多的噪音而不是帮助。
另一种方法是更喜欢使用智能指针,但它们也有自己的陷阱。引用计数和标记清除智能指针将悬空指针变成资源泄漏!并且 unique ptr 没有 "observer" 安全的指针类型。
在其范围结束后的局部指针不复存在。它不能是悬挂指针,因为它不是指针。