今天通过series of SO questions显而易见,我对指针,引用和值的真实性质知之甚少。
请考虑以下代码:
int* p = new int(3);
int& r = *p;
cout << " p = " << p << "\t*p = " << *p << endl;
cout << "&r = " << &r << "\t r = " << r << endl;
delete p;
cout << "&r = " << &r << "\t r = " << r << endl;
int v = 4;
r = v;
cout << "&r = " << &r << "\t r = " << r << endl;
此输出为
p = 0x1001000b0 *p = 3
&r = 0x1001000b0 r = 3
&r = 0x1001000b0 r = 3
&r = 0x1001000b0 r = 4
我不明白的是为什么我第二次打印引用的值时我没有收到错误。与参考值对应的指针已被删除。从我的previous question开始,我几乎确信自己,r = x
这样的任何陈述都会在x
引用的值的位置复制r
。但是,如果是这种情况,则p
和&r
将是不同的地址,对吧?如果我已经在0x100100b0上调用了delete,那么我该如何继续使用呢?
正确或错误:引用与地址值的别名相同。
真或假:如果删除指向与引用值相同的地址的指针(如上所述),则不会发生任何未定义的行为,并且没有人会覆盖该地址,只要参考文献存在。
答案 0 :(得分:11)
即使您没有收到错误,结果仍未定义。未定义的行为意味着任何事情都可能发生,包括您的程序似乎继续正常工作。
引用与地址值的别名相同。
这实际上是正确的。说引用是对象的别名(而不是值)会更正确。
如果删除指向与引用值相同的地址的指针(如上所述),则不会发生未定义的行为,
这也是事实。在尝试使用引用之前,没有已定义的行为。当您尝试使用引用(例如,通过&r
)时,您将获得未定义的行为。
如果在销毁对象后从未尝试使用引用,则没有未定义的行为。
只要引用存在,任何人都不会覆盖该地址。
不,这不正确。一旦对象被销毁,任何引用或指针都无效且无法使用。如果您尝试使用指向已销毁对象的指针或引用,则结果是未定义的。
答案 1 :(得分:1)
引用是对象的别名。如果该对象的生命周期已结束,则对该对象的引用将不再有效。
在您的示例中,在r
操作后使用referecen delete p
导致未定义的行为 - 它“看起来”工作的事实只是巧合 - 在此之后使用r
与在该点之后使用*p
一样无效(并且会产生相同的行为。
例如,修改程序以使用*p
和r
执行相同的操作,如下所示:
#include <iostream>
using namespace std;
int main()
{
int* p = new int(3);
int& r = *p;
cout << " p = " << p << "\t*p = " << *p << endl;
cout << "&r = " << &r << "\t r = " << r << endl;
delete p;
cout << " p = " << p << "\t*p = " << *p << endl;
cout << "&r = " << &r << "\t r = " << r << endl;
int v = 4;
*p = v+1;
cout << " p = " << p << "\t*p = " << *p << endl;
r = v;
cout << "&r = " << &r << "\t r = " << r << endl;
}
输出:
p = 0x3f1730 *p = 3
&r = 0x3f1730 r = 3
p = 0x3f1730 *p = 4134736
&r = 0x3f1730 r = 4134736
p = 0x3f1730 *p = 5
&r = 0x3f1730 r = 4
您会看到在删除对象后使用*p
的行为类似(并且它同样无效)。
在所有情况下,在删除对象后访问该对象是未定义的行为,无论该访问是通过无效指针还是无效引用进行的。
答案 2 :(得分:1)
正确还是错误:引用是相同的 作为a的值的别名的东西 地址。
呃......真的吗?你的术语是什么使它不清楚,但我想我得到了你的要求。
正确还是错误:如果删除指针 与引用的地址相同 价值存在,(如上所述),然后 不会发生未定义的行为,并且 没有人会覆盖那个 地址只要参考 存在。
假。 只有因为你的C ++实现对内存管理没有偏执并且你在“有效性”到期后立即访问它,你才能在未分配内存后读取正确的值。
你知道,在编译时,编译器无法预测你是否会采用这种肮脏的技巧,而且在运行时内存安全性微观管理成本很高。
那么,为什么这是不好的做法,哪些可能出错? 1)如果在你的“删除p”和第3个cout之间进行大量内存繁重的操作,并且有很多“新”和“删除”调用 - 实际内存点“r”引用的可能性很小受到损害(由于内存释放到操作系统,其值已更改或完全不可用 - 这将导致一般保护错误和您的应用程序崩溃)
2)如果你在一些偏执(或资源稀缺)的环境中编译和运行你的应用程序,你将会崩溃,因为系统会跟踪你的应用程序所属的内存,即使是在“int”值的范围内。
如果您需要更多详细信息,可以查找“c ++ heap”和“c ++ memory management”主题。
答案 3 :(得分:0)
第一个是真的:
第二个是假的:
int
时。该块已被释放到堆中以供将来使用,因此不能保证它包含的内容实际上是3或甚至是引用类型的合法值(尽管当然每个可能的值对于int
都是合法的 - - 虽然它可能是一种类型。实际上,由于该值未被其他任何内容覆盖,因此在使用引用时仍会获得“有意义”的结果。然而,这只是纯粹的“巧合”。更新:我在回答第一个问题时错了,重新修改了答案。
答案 4 :(得分:0)
A reference is the same thing as an alias to the value at an address.
真
True or false: If you delete a pointer to the same address as a referenced value resides, (as I do above), then no undefined behavior will occur, and no one will ever overwrite that address as long as the reference exists.
假
int v = 4;
r = v;
编译因为r是对int的引用。
但是,当您删除引用指向的任何内容然后尝试使用它时,它是未定义的行为!
答案 5 :(得分:0)
你的第一个问题有点过于含糊,无法回答。地址值的别名是什么?
第二个肯定是假的。 p指向的内存被释放,可以重用。碰巧它还没有改变。未定义使用r
和r = v
取消引用r并复制v的值(也未定义 - 可能已崩溃)。
当某些内容未定义时,这并不意味着您将收到错误消息。这意味着您获得的任何行为都被视为规范。崩溃或工作都是可接受的“未定义”行为。
答案 6 :(得分:0)
正确或错误:引用与地址值的别名相同。
错误:它与地址无关(这是一个实现细节)。
正确还是错误:如果删除指向与引用值相同的地址的指针(如上所述),则不会发生任何未定义的行为,只要引用存在,就不会覆盖该地址
假。使用对不再存活的变量的引用是未定义的行为。不同类型的变量在不同时间死亡。