2014-12-08 133 views
5

我的问题:指针和动态内存分配

int* x = new int; 
cout << x<<"\n"; 
int* p; 
cout << p <<"\n"; 
p = x; 
delete p; 
cout << p <<"\n"; 

我写这纯粹是我自己了解指针和理解(也迷失在)的动态newdelete

我的XCode可以编译程序并返回结果如下:

0x100104250 
0x0 
0x100104250 

我知道,我只能叫删除动态分配的内存。 但是,我在上面的程序中调用了p上的删除并编译。

任何人都可以向我解释这个吗? 为什么我可以删除p

而且,我发现如果程序更改为以下几点:

int* x = new int; 
int* p; 
cout << p <<"\n"; 
delete p; 
cout << p <<"\n"; 

然后我的Xcode再次编译并返回我:

0x0 
0x0 
Program ended with exit code: 0 

,现在,我得到了完全失去:( 请问谁能解释一下吗? 为什么我可以删除p,因为它跟x没什么关系?


由于Xcode编译成功,我假设上述两个程序对于计算机是正确的。 但是,我认为这又是“仅在动态分配内存上调用删除”的说法。 或者可能,我没有完全理解什么是指针,什么是动态分配的内存。我在网上搜索时发现这个post。 但我不认为这是我的情况。

请帮我一把。


我想问一个问题。关于二叉搜索树的代码是here。 从第28行到第32行,它处理删除一个孩子的节点。 我把这部分代码放在这里,以防万一网页链接不起作用。

否则如果(根 - >左== NULL){ 结构节点*温度=根; root = root-> right; 删除温度; }

正是这些代码导致我问关于指针的问题上面。 按照这篇文章给出的答案。 以下列方式了解代码是否正确?

我不能先将根的父节点链接到根的右子节点。 ,然后删除根节点,因为根节点下的子树也将被删除。 所以我必须创建一个临时指针,指向由root指向的内存插槽。 然后我将根的父节点链接到根的右子节点。 现在,我可以安全地删除由“root”指向的内存插槽(即温度,因为它们都指向相同的内存)。 通过这种方式,我释放内存并保持父母与子女之间的联系。 另外,temp仍然存在,仍然指向“那个”内存插槽。 删除后应该将它设置为NULL吗?

再次提前谢谢大家。

腰封

+1

在你的第二个代码示例中,你是“幸运的”,p'是'0'。由于它未初始化,它可能有任何价值。因为它是'0'(又名'NULL'),所以调用'delete'是有效的(这有助于避免对NULL进行一百万次检查,特别是在处理分配失败的错误情况时,并且您希望清理其余的分配 - 如果所有的指针首先被初始化为NULL,那么你可以删除所有的东西,而不用担心哪个分配失败。 – 2014-12-08 08:22:07

+1

只是一个建议,你总是应该初始化你的指针变量,如int * p = 0;或者int * p = NULL;这是因为在调试版本中,这将为您完成。但在发布版本中,它不会完成。这可以为您节省大量的时间调试。 – user743414 2014-12-08 08:23:24

+1

@ user743414除非您维护遗留代码,否则您应该使用C++ 11,因此使用'int * p = nullptr;'。 (这部分)C++ 11现在已经得到所有主要编译器的支持。 – Angew 2014-12-08 08:24:51

回答

2

是的,你只能在其上通过new分配的内存调用delete。请注意,这是内存的地址(指针的),而不是存储指针的变量。所以,你的第一个代码:

int* x = new int; //(A) 
cout << x<<"\n"; 
int* p; 
cout << p <<"\n"; 
p = x; //(B) 
delete p; //(C) 
cout << p <<"\n"; //(D) 

线(A)动态地(在你的例子输出0x100104250)一些地址分配内存和存储变量x这个地址。内存通过new分配,这意味着delete最终必须在地址0x100104250上调用。

线(B)的地址0x100104250(指针x的值)赋值到指针p。行(C)然后调用delete p,这意味着“释放指向p指向的内存”。这意味着它在地址0x100104250上调用delete,并且一切正常。地址0x100104250上的内存为,通过new分配,因此未通过delete正确分配。您使用不同的变量来存储该值不起作用。

行(D)只是打印出指针的值。 delete作用于指针指向的内存,而不是指针本身。指针的值保持不变 - 它仍然指向相同的内存,内存不再分配。


第二个例子是不同的 - 你打电话delete pp未初始化任何东西。它指向一段随机内存,因此调用delete当然是非法的(从技术上讲,它有“未定义的行为”,并且很可能会崩溃)。

看起来在您的特定示例中,您正在运行调试版本,并且如果您未自行初始化它们,则编译器会“有用”地将局部变量初始化为0。所以它实际上会导致你的第二个例子不会崩溃,因为在空指针上调用delete是有效的(并且什么都不做)。但是该程序实际上有一个bug,因为局部变量通常不会被隐式初始化。

+0

非常感谢您的详细和及时的答案。这让我对指针感觉更舒适。 – Yaofeng 2014-12-08 14:22:16

1
p = x; 

这将使p包含相同的值xp将指向相同的对象x)。所以基本上,当你说

delete p; 

它做删除这是一样的,即通过x提到p的地址。因此,这是完全有效的,因为该地址引用的对象是使用new分配的。

第二种情况: -

合作顺便说一句指针p由编译器为NULL pointer设置(你不应该依赖于)。因此删除该指针是安全的。如果不是NULL指针,你可能会看到崩溃。

+0

是的,它有时会崩溃。我在尝试不同的代码时发现它。我忘了将它包括在我的问题中。非常感谢您向我确认这些信息。 – Yaofeng 2014-12-08 14:10:30

1

好吧,让我们来看看“删除”操作符的文档。据http://en.cppreference.com/w/cpp/memory/new/operator_delete

通过删除表达式调用取消分配以前分配给单个对象存储。除非ptr是空指针或者是以前从operator new(size_t)或operator new(size_t,std :: nothrow_t)的标准库实现中获得的指针,否则此函数的标准库实现的行为是未定义的。

所以会发生什么是:您所呼叫的新的运营商,其分配的sizeof(INT),用于存储int变量字节。内存中的那部分被你的指针x引用。然后你创建另一个指针p,它指向相同的内存地址。当你呼叫删除时,内存被释放。 p和x仍然指向相同的内存地址,但在该位置的现在是垃圾。为了使这更容易理解,我修改您的代码如下:

#include <iostream> 
using namespace std; 

int main() { 
    int * x = new int;    // Allocate sizeof(int) bytes, which x references to 
    *x = 8;       // Store an 8 at the newly created storage 
    cout << x << " " << *x << "\n"; // Shows the memory address x points to and "8". (e.g. 0x21f6010 8) 
    int * p;       // Create a new pointer 
    p = x;       // p points to the same memory location as x 
    cout << p << " " << *p << "\n"; // Shows the memory address p points to (the same as x) and "8". 
    delete p;      // Release the allocated memory 
    cout << x << " " << p << " " 
     << *x << " " << *p << "\n"; // p now points to the same memory address as before, except that it now contains garbage (e.g. 0x21f6010 0) 
    return 0; 
} 

运行后,我得到了以下结果:

0x215c010 8 
0x215c010 8 
0x215c010 0x215c010 0 0 

所以请记住,通过删除您释放内存,但指针仍然指向相同的地址。这就是为什么之后将指针设置为NULL通常是安全的做法。现在希望这样做更有意义:-)

+0

它可以帮助我很多。尽管我的Xcode最终返回了8 8而不是0 0。但我认为我有了主意。 – Yaofeng 2014-12-08 14:36:46

0

在答案之前,您需要了解以下几点。

  1. 一旦你delete一个指针,它需要0分配。它 可以是任何东西。
  2. 您可以毫无损害地删除0NULL
  3. 未定义的行为是可能发生任何事情的行为。该 程序可能会崩溃,它可能会正常工作,就好像什么都没有发生, 它可能会产生一些随机的结果,等等,

不过,我打电话给在上面的程序删除对p与它编译。

任何人都可以向我解释这个吗?为什么我可以删除p?

这是因为您通过x分配了由new分配的内存地址。 (p = x;x(或p)是有效的内存位置,可以删除。

现在x被称为Dangling pointer。因为它指向的内存不再有效。删除后访问x是未定义的行为。

请问谁能解释一下吗?为什么我可以删除p,因为它与x没有任何关系?

这是因为您的p被分配为0.因此,您正在逃避未定义的行为。但不保证未初始化的指针的值将为0NULL。此时它似乎工作正常,但您正在通过未定义的行为在这里涉水。

+0

非常感谢您分享悬挂指针的信息。 – Yaofeng 2014-12-08 14:39:37