C ++:如何验证已删除的指针

时间:2013-04-04 21:07:42

标签: c++

目前我正在大学学习C ++中的指针。我编写了一个程序,它是一个二进制对象树,指向一个链接的子对象列表。如果我甚至是正确的措辞。无论如何,我的程序似乎工作正常,但我无法绕过如何测试指针删除。

例如,我对二叉树的单个对象的删除函数是:

    void EmployeeRecord::destroyCustomerList()
    {
        if(m_oCustomerList != NULL)
        {
            delete m_oCustomerList;
            m_oCustomerList = NULL;           
        }
    }

打印我的树时,所有内容都会填充并正确取下(意味着每次删除节点时树都保持不变)......但是如何确认解除分配的内存会发生什么?我知道,因为我将指针* m_oCustomerList设置为NULL,我可以测试先前填充的对象上的NULL值,但实际内存会发生什么?

我正在使用Visual Studio / C ++并且已经读过调试器将使用从0xCC开始的代码来释放内存......但我似乎无法弄清楚如何使用该信息。

4 个答案:

答案 0 :(得分:7)

请注意您的代码

    void EmployeeRecord::destroyCustomerList()
    {
        if(m_oCustomerList != NULL)
        {
            delete m_oCustomerList;
            m_oCustomerList = NULL;           
        }
    }

简化为:

    void EmployeeRecord::destroyCustomerList()
    {
        delete m_oCustomerList;
        m_oCustomerList = NULL;
    } 

在C ++中调用空指针上的delete运算符是安全的。它什么都不做。换句话说,检查null已经“内置”。

一旦删除了一个对象,它就不再存在,并且指向该对象的指针变为不确定值(因此将该指针的所有副本都空出来并不是一个坏主意。)

在实际的C ++实现中,而不是在抽象意义上,真正在内存中发生的是它继续存在于同一地址,但被标记为空闲,因此它可以是分配用于其他目的。来自程序(可能是完全不相关的模块)或可能来自系统中的另一个程序的分配请求可以获得该内存供自己使用。

指向不再存在的对象的指针的任何使用都是“未定义的行为”。安全验证这种指针的功能确实存在,但它们非常特定于平台,很少完美。

问题在于,虽然实现确认指针是坏的并不是特别困难,但是不可能确认指针是好的。我们可以遍历内存分配器的内部存储器数据结构,以确定某些指针指的是空闲存储。但是如果随后分配了存储怎么办?然后指针不再指免费存储。但它也没有引用分配的原始对象!这被称为“ABA歧义”:因为有些A变成了B,但后来变成了A,与原来的A无法区分。

存在解决ABA歧义的方法(如果不是至少部分完全)。例如,指针变为“胖”,因此除了地址位之外,它们还有一个额外的字段。该字段可以包含序列号,用于标记从分配器返回的指针。现在,当删除并重新分配对象时,指向同一位置的新指针具有不同的序列号:我们有ABA'。指针A变坏了,使它成为B,但是当它复活时,它又变回A'。如果我们要求系统验证A,它将正确地确定A是坏的,因为它没有预期的序列号。指向对象的正确有效指针是A',它与A不匹配。

然而,序列号字段只有很多位宽,它们最终会回绕。所以ABA问题还没有真正解决。良好指针和坏指针的验证只能更加可靠。为了绝对处理ABA问题,系统必须总是发出新指针,这些指针不同于任何仍然可以使用的指针。这意味着永远不会释放任何东西(从而耗尽内存)或实现垃圾收集。 (意思是delete实际上什么都不做:删除的对象被破坏,但是在内存中保留,直到它们被垃圾收集,这在程序不再记住指针的任何副本时发生。此时,程序没有更长时间记住A,所以A可以重新引入,并且没有ABA问题。)

要使所有指针“变胖”,您必须更改整个工具链和运行时:编译器,库等等。还有一些困难,因为大型程序往往有多个内存分配器。如果你问错误的分配器“这个指针是否有效”,它可以说是“这个指针不是来自我的竞技场”。您可以做的另一种方法是创建自己的指针,并在C ++中将它们实现为智能指针。您的指针可以支持is_valid方法,该方法尽可能可靠(以某种方式处理ABA问题:部分地使用某些序列号等,或者通过实现您自己的垃圾收集方案。)

答案 1 :(得分:2)

按标准访问已删除的内存是未定义的行为。例如,如果这是一个多线程应用程序(或者某个其他进程已经将一个线程注入到您的应用程序中),那么新的分配可以在您能够“验证”它之前分配您刚刚解除分配的内存。

答案 2 :(得分:0)

删除内存并将指针设置为NULL后,即使您需要,也无法再访问该内存。所以,没有办法验证它真的消失了。但是,如果你做错了什么并且内存从未被删除,那么它将包含一个内存泄漏,这会导致程序增加它使用的内存量,你可能会将其视为指针未正确处理的症状。 / p>

稍后您可能会了解到,由于std::shared_ptr会在指针超出范围时删除您的对象,因此您不必担心删除指针。以后会更安全,因为您可能会了解异常会导致析构函数永远不会发生内存泄漏。

答案 3 :(得分:-2)

...
... 
delete m_oCustomerList;

// Try using the deleted pointer here
// This should cause a runtime exception
// which means you did free the pointer
m_oCustomerList->someStrMemberVariable = "This will fail"
...
...

毋庸置疑,请勿在实际代码中执行此操作。希望这会有所帮助。