编辑:有关此问题中的代码为何起作用的问题已通过重复标记中的链接问题得到解答。关于字符串文字生命周期的问题在这个问题的答案中得到了回答。
我试图了解const char *
指向的字符串是如何以及何时被解除分配的。
考虑:
const char **p = nullptr;
{
const char *t = "test";
p = &t;
}
cout << *p;
离开内部范围后,我希望p
成为const char *
的悬空指针。但是在我的测试中并非如此。这意味着即使在t
超出范围之后,t
的值实际上仍然有效且可访问。
这可能是由于通过将它绑定到const引用来延长临时的生命周期。但我没有这样的事情,甚至在成员变量中保存对t
的引用,稍后从不同的函数打印值仍然给我正确的值。
class CStringTest
{
public:
void test1()
{
const char *t = "test";
m_P = &t;
test2();
}
void test2()
{
cout << *m_P;
}
private:
const char **m_P = nullptr;
};
那么t
的值的生命周期是多少?我会说我通过取消引用指向超出范围的变量值的指针来调用未定义的行为。但它每次都有效,所以我认为情况并非如此。
在尝试其他类型时,例如QString
:
QString *p = nullptr;
{
QString str = "test";
p = &str;
}
cout << *p;
代码总是正确地打印值,即使它不应该。 str
超出范围及其值,我没有通过将其绑定到const引用来延长其生命周期。
有趣的是,带有QString
的类示例的行为与我期望的一样,test2()
打印出乱码,因为值确实超出了范围而m_P
变成了悬空指针。
那么const char *
的值的实际寿命是多少?
答案 0 :(得分:7)
变量p
和t
是您声明的堆栈变量,因此它们的生命周期在其封闭块的末尾结束。
但是t
的值是字符串文字"test"
的地址,并且它不是您声明的变量,它不在堆栈中。它是一个字符串文字,它是程序中定义的常量(类似于整数文字99
或浮点文字0.99
)。文字不会像你期望的那样超出范围,因为它们不是创建或销毁的,它们只是 。
标准说:
评估字符串文字会产生一个具有静态存储持续时间的字符串文字对象,从上面指定的给定字符初始化。
因此,编译器创建的用于表示文字"test"
的对象具有静态存储持续时间,该持续时间与全局变量和static
变量的持续时间相同,这意味着它不会超出范围堆栈变量。
p
的值是t
的地址,当t
超出范围时, 成为无效指针,但这不是意味着存储在该地址的值突然变得不可访问或被擦除。表达式*p
是未定义的行为,但它似乎有效,因为没有任何东西重用该内存位置,因此*p
仍然包含字符串文字的地址。有关详细信息,请参阅Can a local variable's memory be accessed outside its scope?
答案 1 :(得分:3)
编译器将文字字符串放入静态分配的空间,该空间被加载到虚拟内存的受保护段中,这样这些字符串可以在进程的整个生命周期内共享(值是常量,因此无需采取不断使它们存在的开销)。寻找要解除分配的东西是浪费时间,因为它实际上从未发生过。
变量是堆栈分配的。字符串常量应该被认为是:字符串常量...就像数字3。
答案 2 :(得分:1)
字符串文字在静态存储中分配。
如果你在程序的任何地方提到字符串文字,那就像你做的那样:
static const char someUniqueIdentifier[]="the data";
在全球范围内。
const char* str = "some string";
表示确保"some string"
作为程序静态部分中的常量以null结尾的数组存在,并将str
指向它。
然而,您在第一个示例中引用了自动(= on on stack)指针,而不是静态存储字符串。确实有一段时间限于它的范围,但是当你调用test2()
时,test1()
的范围还没有结束。