理解引用与指针。为什么这样做?

时间:2010-12-08 18:32:07

标签: c++ pointers reference

今天通过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,那么我该如何继续使用呢?

正确或错误:引用与地址值的别名相同。

真或假:如果删除指向与引用值相同的地址的指针(如上所述),则不会发生任何未定义的行为,并且没有人会覆盖该地址,只要参考文献存在。

7 个答案:

答案 0 :(得分:11)

即使您没有收到错误,结果仍未定义。未定义的行为意味着任何事情都可能发生,包括您的程序似乎继续正常工作。

  

引用与地址值的别名相同。

这实际上是正确的。说引用是对象的别名(而不是值)会更正确。

  

如果删除指向与引用值相同的地址的指针(如上所述),则不会发生未定义的行为,

这也是事实。在尝试使用引用之前,没有已定义的行为。当您尝试使用引用(例如,通过&r)时,您将获得未定义的行为。

如果在销毁对象后从未尝试使用引用,则没有未定义的行为。

  

只要引用存在,任何人都不会覆盖该地址。

不,这不正确。一旦对象被销毁,任何引用或指针都无效且无法使用。如果您尝试使用指向已销毁对象的指针或引用,则结果是未定义的。

答案 1 :(得分:1)

引用是对象的别名。如果该对象的生命周期已结束,则对该对象的引用将不再有效。

在您的示例中,在r操作后使用referecen delete p导致未定义的行为 - 它“看起来”工作的事实只是巧合 - 在此之后使用r与在该点之后使用*p一样无效(并且会产生相同的行为。

例如,修改程序以使用*pr执行相同的操作,如下所示:

#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)

第一个是真的:

  • 引用确实是值的别名。引用的具体实现不会改变这一事实,也不应该依赖它。

第二个是假的:

  • 如果删除指向同一地址的指针,则使用以前创建的引用时唯一的确定来源是语言规范。我相信C ++中的情况是“未定义的行为”。在你的例子中发生的是(我在这里假设),引用被实现为指针,因此它仍然指向具有值3的内存块,当被解释为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指向的内存被释放,可以重用。碰巧它还没有改变。未定义使用rr = v取消引用r并复制v的值(也未定义 - 可能已崩溃)。

当某些内容未定义时,这并不意味着您将收到错误消息。这意味着您获得的任何行为都被视为规范。崩溃或工作都是可接受的“未定义”行为。

答案 6 :(得分:0)

  

正确或错误:引用与地址值的别名相同。

错误:它与地址无关(这是一个实现细节)。

  

正确还是错误:如果删除指向与引用值相同的地址的指针(如上所述),则不会发生任何未定义的行为,只要引用存在,就不会覆盖该地址

假。使用对不再存活的变量的引用是未定义的行为。不同类型的变量在不同时间死亡。

  • 自动存储持续时间变量在范围结束时死亡。
  • 静态存储持续时间变量在主要退出后以相反的顺序死亡
  • 动态存储持续时间变量在它们所在的内存上调用delete时死亡。