指向已删除的堆栈内存的指针

Pointer pointing to deleted stack memory

本文关键字:堆栈 指针 内存 删除      更新时间:2023-10-16

我相信我刚才所经历的被称为"未定义行为",但我不太确定。基本上,我在保存类地址的外部作用域中声明了一个实例。在内部层,我在堆栈上实例化了一个对象,并将该实例的地址存储到holder中。

内部作用域转义后,我检查是否仍然可以访问被删除实例的方法和属性。令我惊讶的是,它工作起来没有任何问题。

是否有一个简单的方法来解决这个问题?是否有一种方法可以从列表中清除已删除的指针?

的例子:

std::vector<int*> holder; 
{
    int inside = 12;
    holder.push_back(&inside);
}
cout << "deleted variable:" << holder[0] << endl;

是否有一个简单的方法来解决这个问题?

当然,有很多方法可以避免这类问题。

最简单的方法是根本不使用指针——而是按值传递对象。例如,在您的示例代码中,您可以使用std::vector<int>而不是std::vector<int *>

如果你的对象由于某种原因不能复制,或者你认为复制它们太大了,你可以把它们分配到堆上,并使用shared_ptr或unique_ptr或其他智能指针类自动管理它们的生命周期。(请注意,按值传递对象比您想象的更有效,即使对于较大的对象也是如此,因为它避免了必须处理堆,这可能是昂贵的……而现代cpu在处理连续内存时效率最高。最后,现代c++有各种优化,允许编译器在许多情况下避免实际执行数据复制)

一般来说,保留指向堆栈对象的指针是一个坏主意,除非你100%确定指针的生存期将是它所指向的堆栈对象的生存期的一个子集。(即使这样,这也可能是一个坏主意,因为在你转到下一份工作之后,下一个接手代码的程序员可能不会发现这个微妙的危险,因此在修改代码时很可能无意中引入了悬空指针错误)

内部作用域转义后,我检查是否还可以访问已删除实例的方法和属性。令我吃惊的是

如果对象所在的内存还没有被其他任何东西覆盖,就会发生这种情况——但是如果/当您解引用无效指针时,绝对不要依赖该行为(或任何其他特定行为),除非您愿意花费大量的高质量时间与调试器一起追踪随机崩溃和/或其他奇怪的行为:)

是否有一种方法可以从列表中清除已删除的指针?

原则上,可以向对象的析构函数中添加代码,使其遍历列表,查找指向自身的指针并删除它们。在实践中,我认为这是一种糟糕的方法,因为它消耗了CPU周期,试图从错误中恢复,而更好的设计一开始就不允许这样做。

顺便说一句,这是离题的,但你可能会感兴趣的是,Rust编程语言的设计是通过在编译时捕获它来检测和防止这类错误。也许有一天c++也会有类似的功能

不存在已删除的指针。指针只是一个数字,表示进程虚拟地址空间中的某个地址。即使栈帧已经没有了,保存它的内存仍然是可用的,因为它是在线程启动时分配的,所以从技术上讲,它仍然是一个有效的指针,有效的是,你可以解引用它并得到一些东西。但是由于它所指向的对象已经消失,有效的术语将是悬空指针。道德是,如果你有指向堆栈框架中的对象的指针,没有办法确定它是否有效,甚至没有使用像IsBadReadPtr这样的函数(Win32 API只是例如)。防止这种情况的最佳方法是避免返回和存储指向堆栈对象的指针。

但是,如果您希望跟踪堆分配的内存并在不再使用后自动释放它,您可以使用智能指针(std::shared_ptr, boost::shared_ptr等)。