C++指针删除问题,仍可访问数据

5
我不太理解为什么这些指针是可访问的...任何帮助都将不胜感激。
#include <iostream>

class Wicked{
public:
    Wicked() {};
    virtual ~Wicked() {};

    int a;
    int b;
};


class Test
{
public:
    Test() {};
    virtual ~Test() {};

    int c;

    Wicked * TestFunc()
    {
        Wicked * z;
        c = 9;
        z = new Wicked;
        z->a = 1;
        z->b = 5;
        return z;
    };
};

int main()
{
    Wicked *z;

    Test *t = new Test();
    z = t->TestFunc();

    delete z;
    delete t;

    // why can I set 'z' when pointer is already destroyed?
    z->a = 10;

    // why does z->a print 10?
    std::cout << z->a << std::endl;

    // why does t->c exist and print correct value?
    std::cout << t->c << std::endl;

    //------------------------------------------------------

    int * p = new int;
    *p = 4;

    // this prints '4' as expected
    std::cout << *p << std::endl;

    delete p;

    // this prints memory address as expected
    std::cout << *p << std::endl;

    return 0;
}

1
我不太明白问题是什么。 - Tomas
除非您重新表达您的问题,否则我猜它很快就会被关闭。 - xtofl
在删除 'z' 和 't' 指针之后,它们仍然打印出值。 - user1003948
2
OP问为什么在删除指针后,他仍然可以获取旧值,即内存仍然可访问。我认为这个问题没有问题。 - Cratylus
这是非常正常的行为,不是吗?delete操作符只是释放内存,但指针本身和内存仍然保持不变 - 为什么会改变呢? - Tomas
6个回答

11

删除指针时,不会清空任何内存,因为这样做需要CPU周期,而这并不是C++的主要目的。你有一个悬空指针,可能存在微妙的错误。像这样的代码有时可能能够工作多年,只有在程序中其他地方进行了一些次要更改后才会在将来某个时间点崩溃。

这是为什么当您删除它们所指向的内存时,应该将指针设置为NULL的很好的原因,这样如果您尝试引用指针,则会立即出现错误。如果指向的内存包含机密信息(例如明文密码),您不希望程序的其他部分(可能是用户界面)可以访问,那么有时清除指向内存的内容可能是个好主意,可以使用memset()等函数。


2
将指针设置为NULL可能会导致其他类型的错误被隐藏(潜在地长时间存在)。最好使用智能指针,甚至不要陷入这种情况。此外,不能保证对空指针进行解引用会生成错误(与其他位置一样),最好不要依赖此来检测错误。 - Martin York
我猜你并没有真正阅读你所链接的那个问题,原帖中在“解决方案”部分中提到,“所讨论的指针并不是NULL”,结果证明他期望delete将指针置为NULL。 - Benj
1
糟糕,你是对的,我没有读它,我只是假设人们足够聪明地写一个好标题。我想人们比我想象的要愚蠢(包括我自己)。 - Martin York
这是他认为将其设置为NULL是浪费时间的理由。但将其设置为NULL也隐藏了双重解引用问题。这可能会在调试模式下被检测到。 - Martin York
我不同意。我认为它隐藏了更多的问题,而不是帮助预防。主要是因为我同意安德烈的观点,它并没有起到任何预防作用。 - Martin York
显示剩余21条评论

10

那就是未定义行为,任何事情都有可能发生。你这次很幸运。或者不幸,因为最好出现运行时错误!下一次也许会出现运行时错误。

关于为什么会出现特定的未定义行为,这样的推理并没有太大用处。最好坚持关于哪些你可以推理的明确定义行为。


未定义行为,需要引用 - Talespin_Kit
@Talespin_Kit 你认为这是实现定义而不是未定义的吗? - David Heffernan
我正在寻找C++标准的引用。我找不到任何权威来源。 - Talespin_Kit
@Talespin_Kit 哪个标准的版本?这个问题已经超过8年了。 - David Heffernan

6

C++无法阻止你在内存的任意位置进行写入操作。当你使用newmalloc分配内存时,C++会在内存中找到一些未使用的空间,并将其标记为已分配(以免它再次被误分配),然后将其地址给予你。

但是,在你使用delete释放了这段内存之后,C++会将其标记为空闲状态,并可能将其分配给任何请求它的人。你仍然可以对其进行读写操作,但此时其他人可能正在使用它。当你向该内存位置写入数据时,你可能会覆盖掉你先前分配的某个值。


5

在这里

// why can I set 'z' when pointer is already destroyed?
z->a = 10;

z仍然指向一个内存位置。
但它不再属于你。你已经把它传递给了delete,说让它处理这个指针。它的行为不再是你关心的事情。就像当你卖掉你的车时,它仍然存在,但不再是你的,因此打开车门并查看可能是可能的,但可能会导致警方逮捕你。

删除指针也是同样的道理,内存仍然存在,但不再属于你。
如果你查看其中的内容,它可能会运行,但也可能会导致段错误,因为库已经刷新了页面(你永远不知道)。


5
delete z;只是释放指针z所指向的内存,它并不销毁指针本身。
因此,z变成了一个野指针

3

因为删除一个内存块并不会将指向它的所有指针的值都清零。删除内存只是做了一个标记,表明该内存可用于分配给其他用途。在此之前,内存可能看起来完好无损,但您不能保证它,并且在某些编译器/运行时/体系结构组合中,您的程序将表现出不同的行为 - 甚至可能崩溃。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接