我刚刚学习了C ++类中的指针和删除指针。我自己尝试了这段代码
# include<iostream>
using namespace std;
int main(){
int num = 10;
int *p = new int;
p = #
cout << *p << endl;
delete p;
cout << num << endl;
return 0;
}
删除指针p
后,我无法打印num
的值。但是如果我在程序的最后删除p
,cout << num << endl;
会给我10.任何人都知道我在哪里跑了?
答案 0 :(得分:11)
你先泄漏了一个指针
int *p = new int;
p = # // You just leaked the above int
然后非法删除了你没有new
delete p; // p points to num, which you did not new
答案 1 :(得分:2)
你已经收到了一些指出错误的好答案,但我读到了对堆与堆栈变量的分配和释放的更深层次的误解。
我意识到这已成为一个相当长的帖子,所以也许如果人们认为它有用,我应该把它作为社区Wiki放在某个地方。希望它澄清了你的一些困惑。
堆栈是有限且固定大小的存储。如果您没有另行指定,则会在此处创建局部变量,并且在不再需要它们时将自动清除它们。这意味着您不必明确分配它们 - 它们将在您声明它们的那一刻开始存在。你也不必解除分配 - 当它们超出范围时它们会死亡,松散地说:当你到达它们被定义的块的末端支撑时。
int main() {
int a; // variable a is born here
a = 3;
a++;
} // a goes out of scope and is destroyed here
指针只是一个变量,而不是保持整数的int
或保持真/假值的bool
或保持浮点的double
,一个指针保存一个内存地址。您可以使用地址运算符&
:
{
int a = 3, b = 4;
int* p = &a; // p's value is the address of b, e.g. 0x89f2ec42
p = &b; // p now holds the address of b, e.g. 0x137f3ed0.
p++; // p now points one address space further, e.g. 0x137f3ed4
cout << p; // Prints 0x137f3ed4
} // Variables a, b and p go out of scope and die
请注意,您不应该认为a
和b
是&#34;&#34;在记忆中彼此相对,或者如果p
具有&#34;使用过的&#34;地址作为其值,您还可以在p + 1
处读取和写入地址。
您可能知道,您可以使用指针间接运算符访问地址的值,例如
int* p = &a; // Assume similar as above
*p = 8;
cout << a; // prints 8
cout << &a << p; // prints the address of a twice.
请注意,即使我使用指针指向另一个变量,我也不需要清理任何内容:p
只是a
的另一个名称,从某种意义上说,由于p
及其指向的内容都会自动清理,因此我无需在此处执行此操作。
堆内存是一种不同类型的内存,理论上它的大小不受限制。您可以在此处创建变量,但您需要明确告诉C ++您要这样做。执行此操作的方法是调用new
运算符,例如new int
将在堆上创建一个整数并返回地址。使用分配的内存可以做一些合理的事情的唯一方法是保存这个给你的地址。你这样做的方法是将它存储在指针中:
int* heapPtr = new int;
现在您可以使用指针访问内存:
*heapPtr = 3;
cout << heapPtr; // Will print the address of the allocated integer
cout << *heapPtr; // Will print the value at the address, i.e. 3
问题是堆上创建的变量会继续存在,直到你说你不再需要它们为止。您可以通过在要删除的地址上调用delete
来执行此操作。例如。如果new
给了你0x12345678
,那么在你致电delete 0x12345678
之前,记忆就会属于你。因此,在退出范围之前,您需要调用
delete heapPtr;
并且您将告诉您的系统地址0x12345678再次可用于下一个出现的代码并且需要堆上的空间。
现在这里有一个危险,那就是你可能会丢失手柄。例如,请考虑以下事项:
void f() {
int* p = new int;
}
int main() {
f();
cout << "Uh oh...";
}
函数f
在堆上创建一个新整数。但是,存储地址的指针p
是一个局部变量,只要f
退出就会被销毁。一旦你回到main函数中,你突然有了 no 的想法,你所分配的整数就在那里,所以你再也无法在它上面调用delete
了。这意味着 - 至少在程序的持续时间内 - 您将拥有根据您的操作系统被占用的内存,因此您不能将其用于其他任何内容。如果您经常这样做,即使您无法访问任何内容,也可能会耗尽内存。
这是您正在犯的错误之一:
int* p = new int;
在堆上分配一个新整数并将地址存储在p
中,但是在下一行中
p = #
你用另一个地址覆盖它。此时,您将丢失堆上的整数,并且您已创建内存泄漏。
除了不经常释放内存(即不是一次),你可以做的另一个错误是经常释放它。或者,更确切地说,您已经告诉您的操作系统不再需要它, 后,您可以犯错误访问内存。例如,请考虑以下事项:
int main() {
int* p = new int;
*p = 10;
delete p; // OK!
*p = 3; // Errr...
}
最后一行是非常错误的!您刚刚返回了调用delete
时分配的内存,但该地址仍存储在p
中。在您调用delete
之后,您的操作系统可以随时重新分配内存 - 例如,在另一个线程可以调用new double
之后立即获取相同的地址。那时,如果你写*p = 3
,那么你正在写入不再属于你的的内存,这可能会导致灾难,如果你碰巧覆盖内核中的核心位置&#39 ;存储启动代码,或者根本不会发生任何事情,因为在程序结束之前,内存从未用于任何其他内容。
我们得出以下结论:在堆栈上分配的内存不是您要求的,而不是您的释放。在堆上分配的内存是您自己声明的,但必须也只发布一次。
以下示例不正确:
{
int a = 3;
int* p = &a;
delete a;
} // Uh oh... cannot clean up a because it is not ours anymore!
{
int* p = new int;
delete p;
*p = 3; // Uh oh, cannot touch this memory anymore!
delete p; // Uh oh, cannot touch this memory anymore!
}
嗯,说实话,你只是&#34;幸运&#34;那里。实际上,操作系统管理内存的方式通常很懒散。当你告诉它时,我想要一些记忆&#34;它不会为你归零。这就是写
的好主意int main() {
int a;
a = a + 3;
cout << a;
}
您在内存中的某处分配了变量a
,但a
的值将是该内存位置中的任何内容。它可能是零或一些随机数,取决于启动计算机时位数的下降。这就是为什么你应该总是初始化变量:
int a = 0;
同样地,当你说'#34;我不需要这个记忆&#34;操作系统不会将其归零。这将是缓慢和不必要的:它需要做的就是将内存标记为&#34;可以自由重新分配&#34;。因此,如果您将其退回并立即访问它,那么它尚未重新分配的可能性非常大。因此
int* p = new int;
*p = 10;
delete p;
cout << *p;
保证不打印10.地址p
指向可能已经(部分)由delete
之后的其他人(部分)拍摄(并初始化!) 。但是如果它没有,那么内存仍将包含值10,所以即使它不再是你的,C ++仍然允许你访问它。基本上,当你使用指针时,你是在告诉它&#34;相信我,我是程序员 - 你不需要做各种慢速检查以确保我住在哪里我应该是,而我自己要小心!&#34;
答案 2 :(得分:1)
using namespace std;
int main(){
int num = 10; // a) an int is created on stack
int *p = new int; // b) another int is allocated on heap
p = # // c) address of int from stack is assigned to p and the pointer
// allocated in b) is leaked: as nothing points to it anymore,
// it can't be deleted
cout << *p << endl;
delete p; // d) deleting a pointer that was not dynamically allocated
// and is pointing to stack.
cout << num << endl;
return 0;
}