协慌网

登录 贡献 社区

C ++ 中指针变量和引用变量之间有什么区别?

我知道引用是语法糖,因此代码更容易读写。

但有什么区别?


以下答案和链接摘要:

  1. 指针可以重新分配任意次数,而绑定后无法重新分配引用。
  2. 指针可以指向任何地方( NULL ),而引用总是指向一个对象。
  3. 您无法使用指针获取引用的地址。
  4. 没有 “参考算术”(但是您可以获取引用所指向的对象的地址,并在其上执行指针算法,如&obj + 5 )。

澄清一个误解:

C ++ 标准非常谨慎,以避免规定编译器如何实现引用,但每个 C ++ 编译器都将引用实现为指针。也就是说,声明如下:

int &ri = i;

如果它没有完全优化 ,则分配与指针相同的存储量,并将i的地址放入该存储器中。

因此,指针和引用都使用相同数量的内存。

作为基本规则,

  • 使用函数参数和返回类型中的引用来提供有用的自记录接口。
  • 使用指针实现算法和数据结构。

有趣的读物:

答案

  1. 可以重新分配指针:

    int x = 5;
    int y = 6;
    int *p;
    p =  &x;
    p = &y;
    *p = 10;
    assert(x == 5);
    assert(y == 10);

    引用不能,必须在初始化时分配:

    int x = 5;
    int y = 6;
    int &r = x;
  2. 指针在堆栈上有自己的内存地址和大小(x86 上为 4 个字节),而引用共享相同的内存地址(使用原始变量),但也会占用堆栈上的一些空间。由于引用与原始变量本身具有相同的地址,因此可以将引用视为同一变量的另一个名称。注意:指针指向的内容可以在堆栈或堆上。同上一个参考。我在这个陈述中的主张并不是指针必须指向堆栈。指针只是一个保存内存地址的变量。此变量位于堆栈上。由于引用在堆栈上有自己的空间,并且因为地址与它引用的变量相同。更多关于堆栈与堆 。这意味着编译器不会告诉您存在引用的真实地址。

    int x = 0;
    int &r = x;
    int *p = &x;
    int *p2 = &r;
    assert(p == p2);
  3. 您可以指向指向提供额外间接级别的指针的指针。而引用仅提供一个间接层。

    int x = 0;
    int y = 0;
    int *p = &x;
    int *q = &y;
    int **pp = &p;
    pp = &q;//*pp = q
    **pp = 4;
    assert(y == 4);
    assert(x == 0);
  4. 指针可以直接指定为nullptr ,而引用则不能。如果你足够努力,并且知道如何,你可以创建一个引用nullptr的地址。同样,如果你足够努力,你可以引用一个指针,然后该引用可以包含nullptr

    int *p = nullptr;
    int &r = nullptr; <--- compiling error
    int &r = *p;  <--- likely no compiling error, especially if the nullptr is hidden behind a function call, yet it refers to a non-existent int at address 0
  5. 指针可以遍历数组,您可以使用++转到指针指向的下一个项目, + 4转到第 5 个元素。无论指针指向的对象是什么大小。

  6. 需要使用*取消引用指针以访问它指向的内存位置,而可以直接使用引用。指向类 / 结构的指针使用->来访问它的成员,而引用使用 a .

  7. 指针是保存内存地址的变量。无论引用如何实现,引用都具有与其引用的项相同的内存地址。

  8. 引用不能填充到数组中,而指针可以是(由用户 @litb 提及)

  9. Const 引用可以绑定到临时值。指针不能(不是没有一些间接):

    const int &x = int(12); //legal C++
    int *y = &int(12); //illegal to dereference a temporary.

    这使得const&更安全的用于参数列表等等。

什么是 C ++ 参考( 适用于 C 程序员

引用可以被认为是一个常量指针 (不要与指向常量值的指针混淆!)和自动间接,即编译器将为您应用*运算符。

必须使用非 null 值初始化所有引用,否则编译将失败。获取引用的地址既不可能 - 地址运算符将返回引用值的地址 - 也不可能在引用上进行算术运算。

C 程序员可能不喜欢 C ++ 引用,因为当间接发生时,或者如果参数通过值或指针传递而不查看函数签名,它将不再是显而易见的。

C ++ 程序员可能不喜欢使用指针,因为它们被认为是不安全的 - 虽然引用并不比常量指针更安全,除了在最微不足道的情况下 - 缺乏自动间接的便利性并带有不同的语义内涵。

请考虑C ++ FAQ 中的以下语句:

即使参考使用底层汇编语言的地址经常被实现,请不要以为引用作为好笑的看着指针指向的对象。参考对象。它不是指向对象的指针,也不是对象的副本。这对象。

但如果参考确实是对象,那么怎么会有悬空引用呢?在非托管语言中,引用不可能比指针更 “安全” - 通常只是不能跨范围边界可靠地对值进行别名!

为什么我认为 C ++ 引用很有用

来自 C 背景,C ++ 引用可能看起来像一个有点愚蠢的概念,但是在可能的情况下仍然应该使用它们而不是指针:自动间接方便的,并且在处理RAII时引用变得特别有用 - 但不是因为任何感知的安全性优点,而是因为它们使写作惯用代码不那么尴尬。

RAII 是 C ++ 的核心概念之一,但它与复制语义非常简单地交互。通过引用传递对象避免了这些问题,因为不涉及复制。如果语言中没有引用,则必须使用指针,这些指针使用起来比较麻烦,因此违反了语言设计原则,即最佳实践解决方案应该比替代方案更容易。

如果你想变得非常迂腐,你可以用引号做一件事,你不能用指针做:延长临时对象的生命周期。在 C ++ 中,如果将 const 引用绑定到临时对象,则该对象的生命周期将成为引用的生命周期。

std::string s1 = "123";
std::string s2 = "456";

std::string s3_copy = s1 + s2;
const std::string& s3_reference = s1 + s2;

在此示例中,s3_copy 复制作为串联结果的临时对象。而 s3_reference 本质上成为临时对象。它实际上是对临时对象的引用,该临时对象现在具有与引用相同的生命周期。

如果你在没有const情况下尝试这个,它将无法编译。您不能将非 const 引用绑定到临时对象,也不能为此处获取其地址。