问题 const指针(不是指向const的指针)和引用之间的功能区别是什么?


我目前正在从C ++ Primer学习C ++,它解释了引用如何是另一个变量名的别名。它还解释了指针如何指向另一个变量。它指出指针和引用之间的区别在于可以重新分配指针而引用不能。

在下面的代码示例中,我可以用指针或引用做什么,而我不能用另一个做?

double pi = 3.14;
double &piRef = pi;
double *const piPnt = π

//both of these examples are valid and do the same thing
piRef = 3.14159;
*piPnt = 3.14159;

//however, if I attempt to reassign what the pointer points to, it is illegal.
//this is the same as with a reference, as a reference can't be reassigned either
double tau = 6.28;
piPnt = τ

我知道每个的内部差异(例如指针是一个对象,一个引用不是)。我感兴趣的是这些差异对程序员的影响如何超出略有不同的语法。因此,这不是重复的 这个 接受的答案仅涉及内部差异的问题。


10424
2017-10-05 06:08


起源

@juanchopanza我的例子中的指针是一个const指针,因此无法重新分配。 - john01dav
但您可以在引用上使用赋值。请记住它是其他东西的别名。 - juanchopanza
@juanchopanza我可以重新指定指针指向的值。例如,两者 piRef = 3.14159 和 *piPnt = 3.14169 是有效的。指针可以是const而不指向const对象。 - john01dav
恩,那就对了。没有太大的区别,除了指针本身就是一个对象,所以你可以,例如,获取它的地址。引用没有自己的地址。 - juanchopanza
您可以将const引用绑定到临时。 - n.m.


答案:


从功能的角度来看,指针和引用确实是相同的......它们引用一个对象而不是该对象的副本。

除了不能重新引用引用之外唯一真正的区别是指针可以是 NULL (即,它可以指向任何东西),而假定引用总是引用一个对象。

从技术上讲,你实际上可以得到一个引用没有对象的引用(例如传递 *p 期望参考的函数 p 是空指针)但这是“未定义的行为”。

换句话说,指针比引用更“灵活”,这允许编译器忽略

  • 引用可以更改它引用的对象
  • 引用可以没有对象

在某些情况下,这可以产生更快的代码(但是对于第二点,取消引用空指针是未定义的行为而不是运行时错误;因此,编译器不会强制生成在这种情况下执行特殊操作的代码:指针从代码生成的角度来看,实际上可以指向没有对象确实无关紧要,因为它在程序员和编译器之间的契约中永远不会发生这种情况。

“价格”支付重新装配和拥有的额外灵活性 NULLs是语法(有点无偿)更烦人。

如果指针无法重新分配(因为指针本身是 const)然后除了更详细但显式的语法之外没有任何实际差异。这是因为尽管是一个对象a const-declared指针 即使使用别名也无法更改

从语法的角度来看,你可以采用的是地址 const 指针,将地址强制转换为非const指针的地址并更改指向的值,这样的操作将是未定义的行为,无论发生什么(例如忽略赋值),编译器如果被告上法庭将是正确的: - )


6
2017-10-05 06:16



在什么情况下我想使用const null指针?如果它没有用,它可能也不存在 - 当然它不相关。 - john01dav
你的两个条件是编译器可以忽略两个我认为不存在的东西。引用不能指向非对象,也不能更改它引用的内容。 - john01dav
@ john01dav:完全正确。 C ++中的这些东西是不可能的(一个语法和强制编译时间,另一个因为它是不受支持和未定义的行为)所以编译器可以生成比它们可能生成的代码更好的代码。例如 ++x多次在一个功能中 x 是一个 int& 不需要重新加载引用的整数的地址,而使用 (*p)++ 哪里 p 是一个指针,编译器也必须考虑到这一点 p 可能已经改变了(也可能是因为别名)。 - 6502
我怀疑它们存在的部分原因是它们存在C;引用只是一种更方便的语法,但明确禁止某些内容没有什么意义,因为表达它的等效方式存在。但它没有用,这并不完全正确 - 就像答案所说的那样,你不能有空引用,但你可以有空指针,所以它们可以与Java中的最终引用相媲美,并且它们确实被使用,如果只是偶尔。 - jaymmer
@ 6502如果p是一个const指针,则不需要考虑p可能已经改变了(double *const p = &someObject;); - john01dav


我可以用指针或引用做什么,我不能用另一个做?

引用允许您编写某些构造函数和重载运算符:

class X
{
    // copy constructor
    X(const X& a);

    // move constructor
    X(X&& a);

    // copy assignment operator
    X& operator=(const X& a);

    // move assignment operator
    X& operator=(X&& a);
}

(实际上,运算符重载是将引用引入C ++的动机用例。)

经常被忽视的是现代C ++的区别 X& (对左值的引用)和 X&& (对rvalue的引用),但没有指向rvalue的指针。


5
2017-10-05 06:57





我可以用指针或引用做什么我不能做的   其他?

double *const piPnt = π

以上陈述

marks piPnt as a read only variable in memory layout

所以, piPnt = &xyz 现在会抛出一个错误。

但是更改指针指向的地址处的值仍然有效。

那是 , *piPnt = 56 很好。

Const指针在需要引用相同内存(端口映射)的嵌入式系统中非常有用。这是一次映射,常量指针在这里很有用。

现在关于参考:

double &piRef = pi;

您无法在C ++中重新初始化引用。您可以为它引用的对象指定不同的值。永远是这个参考的同一个对象。这就是你在你的例子中所做的。

piRef = 3.14159;

之后不能更改引用以引用另一个对象   初始化。请注意,处理引用的初始化   与赋值完全不同。论证传递(5.2.2)和   函数值返回(6.6.3)是初始化。

一些引用有用的地方:

  1. 指针不能指向临时工,标准明确禁止这样做。引用可以绑定到临时对象。
  2. 指针可以为NULL,而假定引用始终引用对象。您仍然可以从返回引用的函数返回null,编译器不会抱怨它,但这是自杀。

1
2017-10-05 06:38



您似乎只是提到了const指针和引用之间的相似性以及非const指针和引用之间的差异。我正在寻找const指针和引用之间的差异。 - john01dav
因此,现在编辑它。 - basav