在此代码中,即使它不超出范围,我也会引用本地变量b
。但是我从同一函数中进行操作,所以它可能仍在堆栈上,对吗?我运行了该程序,但我想知道它是否可以保证在所有实施方面工作。
#include <iostream>
void main()
{
int* a;
{
int b = 5;
a = &b;
}
std::cout << *a;
}
no,那是不保证工作的。a
一旦退出了内部范围,就会悬空,因此对它的任何解雇都会导致不确定的行为,而没有任何保证。
这里的问题不是 b
不超出范围。b
的 lifetime 已经结束。范围是关于对象的名称的范围。生命周期是关于对象存在(在计算模型中)。
从技术上讲,当您使用*a
引用它时,对象b
不存在。用来代表它的字节可能恰好在内存中仍未变化,并且有时使用*a
访问它们可能会正常工作,尤其是在未打开优化的情况下,但这是未定义的行为。
即使其名称不在范围中,仍然可以访问一个对象。这是一个对象在其一生中可访问的示例,即使它不在范围中:
void foo(void)
{
int b;
bar(&b);
}
在此代码中,函数bar
可以访问b
,即使它在foo
中看不到b
的名称。尽管Control离开创建b
的块,但块的执行仅被暂停而不是终止。因此,即使函数bar
执行,b
仍然存在。因此,b
将不在范围内,但访问将在其寿命中。
规格说
每个对象的实例具有自动存储持续时间(3.7.3)与每个条目中的块相关联。这样的对象存在并保留其在块执行期间的最后存储值,而块被暂停(通过函数或接收信号的呼叫)。
b
是具有自动存储持续时间的对象。因此,当您在对象的块外面时,对象不再存在。我相信,您 can 跳跃之前,请继续写信,但如果您跳出其块,则不会。这太伸展了。
我组合了这个示例,以帮助证明基于典型的基于帧的堆栈中的内存分配发生了什么。请参阅:当堆栈变量超出范围时是否会划分?这个问题被重复地关闭。
在testerA
中,我们捕获了 local 变量a
的地址,以供将来使用。我们对testerB
做了类似的操作,只有这次,我们捕获了 second 整数变量的地址, b
。请注意,我们用值44
覆盖a
的内存 - 在此功能中称其为f
。
您可以在调用功能观察发生的事情后立即检查这些值。我们从字面上重复使用相同的内存地址 - 但是无法保证数据从先前的函数调用中所处的格式。
检查堆栈
#include <iostream>
int* aPtr = nullptr;
int* bPtr = nullptr;
int testerA()
{
int a = 3;
aPtr = &a;
}
int testerB()
{
int f = 44;
int b = 5;
bPtr = &b;
}
int main()
{
using namespace std;
testerA();
int test1 = *aPtr;
testerB();
int test2 = *aPtr;
int test3 = *bPtr;
cout << "A: " << test1 << " -> " << test2 << endl;
cout << "B: " << test3 << endl;
cout << "aPtr: " << hex << aPtr << endl;
cout << "bPtr: " << hex << bPtr << endl;
return 0;
}
样本输出
A: 3 -> 44
B: 5
aPtr: 0x7fff9e58609c
bPtr: 0x7fff9e586098