考虑以下示例
class Foo{
public:
Foo(int i):x(i){}
int x;
};
void
bar(Foo *p2)
{
delete p2;
p2 = new Foo(2);
}
int
main()
{
Foo *p1 = new Foo(1);
cout<<p1->x;
bar(p1);
cout<<p1->x;
}
据我所知,指针变量存储在堆栈中,并包含一个地址,指向堆栈中“指向”的动态分配的内存。现在,当我传递指向函数的指针时,在堆栈上创建第二个指针,指向与第一个指针相同的内存地址。当我在bar()中删除p2并分配新内存时,p1和p2应该指向不同的地址,对吗?
但是,如果我编译这段代码,我得到1和2作为输出。这是因为p2设法分配了p1已经指向的相同内存单元,还是我错过了什么?
答案 0 :(得分:1)
这是因为p2设法分配了p1已经指向的相同内存单元格,还是我错过了什么?
你什么都没错过。您已正确识别此程序的行为未定义。根据定义,它可以打印任何内容,包括2
。
在你的跑步中,只有巧合(很可能,你确定的巧合)导致它打印2
。您可以通过在new int(47)
之后和现有delete
之前添加new
来破坏这种巧合。
如果您希望程序按照它的方式运行,但通过已定义的行为,您可以对bar
的定义进行小的更改:
void
bar(Foo*& p2)
这会通过引用而不是按值传递参数。通过执行此操作,p2
内变量bar
的更改将反映在`main p1
内。
答案 1 :(得分:0)
除了你的示例之外,指针变量不一定在堆栈上,它们引用的地址不一定在堆上,(但new和malloc都返回堆上的地址)。指针变量和它引用的地址都可以在堆栈,堆,全局/静态变量区域甚至ROM上。你的解释的其余部分是正确的,但你观察到的行为是不确定的,期望它可以重现是非常危险的。任何更改,不同的运行时版本,多线程情况,在不同体系结构上编译或使用其他编译器都可以完全更改结果:随机值或崩溃。