关于c ++:指向引用的指针和指针引用之间的区别

Difference between pointer to a reference and reference to a pointer

指向指针的指针、指向指针的指针和指向C++中指针的指针之间的区别是什么?

哪一个比另一个更受欢迎?


首先,对指针的引用类似于对任何其他变量的引用:

1
2
3
4
5
6
7
void fun(int*& ref_to_ptr)
{
    ref_to_ptr = 0; // set the"passed" pointer to 0
    // if the pointer is not passed by ref,
    // then only the copy(parameter) you received is set to 0,
    // but the original pointer(outside the function) is not affected.
}

一个引用指针在C++中是非法的,因为与指针不同,引用只是一个概念,它允许程序员别名别名。指针是内存中具有其他内容地址的位置,但引用不是。

现在,如果您坚持将引用作为指针来处理,最后一点可能不太清楚。例如。:

1
2
3
4
5
6
7
8
int x;
int& rx = x; // from now on, rx is just like x.
// Unlike pointers, refs are not real objects in memory.
int* p = &x; // Ok
int* pr = ℞ // OK! but remember that rx is just x!
// i.e. rx is not something that exists alone, it has to refer to something else.
if( p == pr ) // true!
{ ... }

从上面的代码中可以看到,当我们使用引用时,我们不会处理与它所指内容分离的内容。所以,引用的地址就是引用的地址。这就是为什么在你所说的情况下,没有所谓的参考地址。


指向指针的指针

C++中的指针只是一个存储内存位置的值(一般是32位值)。

假设您有一个用户输入的整数值(十六进制的78==0x4E)。

它将以与此类似的方式存储在内存中(我特意为本例简化了一些事情):

1
2
Memory address    Value
0x12345678        0x0000004E

如果您想要创建一个指向这个值的"指针",它在内存中会像这样:

1
2
Memory address    Value
0x22334455        0x12345678

在存储器地址0x22334455处,您现在有一个值为0x12345678的"指针",或者存储用户输入整数值(0x4E的存储器地址。

假设您想要创建一个指向这个指针值的"指针"。如下所示:

1
2
Memory address    Value
0x11335577        0x22334455

现在,您在内存中有了一个新的"指针"值,它存储先前定义的指针值的内存地址。

指针可以无限期地这样创建-键记住指针只是编译器解释为内存位置的另一个值(它提供了各种访问语义,如*->,这是"指针"类型特有的)。

对指针的引用

一个引用可以被认为是另一个真实对象的视图或别名。创建对名为myReference的指针的引用时,只需定义一个名为myReference的新名称,该名称可用于访问以前在内存中定义的指针。

在内部,引用是使用指针实现的,但这超出了问题的范围。

引用对C++中的其他类型有限制,例如,在创建时,必须始终初始化引用"引用"给实际对象,而指针可能指向无效或未初始化的内存。

指向引用的指针

这不存在。如前所述,引用只是另一个对象的别名。你不能"指向"一个引用,因为它本身不是一个对象,而只是一个真实对象的另一个名称。

当然,您可以有一个指向引用所引用对象的指针。但现在我们又回到了普通指针领域。

关于参数的说明

当您将一个参数一个值地传递给一个方法或例程时,实际上是将对象的"副本"传递给该方法。当例程返回时,对该例程中的值所做的任何更改都将丢失,因为该参数将在该例程的上下文中被视为局部变量。

如果要修改传入的参数,以便客户端(调用)代码可以访问更改,则必须通过指针或引用传递参数。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void myMethod(int myValue)
{
    // NOTE: This change will be lost to the caller!
    myValue = 5;
}

void myMethod2(int* myValue)
{
    // Correct way of modifying pointer parameter value
    *myValue = 5;
}

void myMethod3(int& myValue)
{
    // Correct way of modifying reference parameter value
    myValue = 5;
}

现在假设您的方法想要为指针分配内存。你可能会想这样做:

1
2
3
4
5
6
void myMethod4(int* myValue)
{
    // Warning: You will lose the address of the allocated
    // memory when you return!
    myValue = new int[5];
}

但请记住,您在这里修改的是指针值的副本,而不是真正的指针值。由于您希望修改此例程中的指针,而不是指针"指向"的值,因此需要将其作为"指向指针的指针"或"指向指针的引用"传递:

1
2
3
4
5
6
7
8
9
10
11
12
13
void myMethod5(int** myValue)
{
    // Correct way of allocating memory in a method
    // via pointer-to-pointer
    *myValue = new int[5];
}

void myMethod6(int*& myValue)
{
    // Correct way of allocating memory in a method
    // via reference-to-pointer
    myValue = new int[5];
}

在下面的2个示例中,调用myMethod5myMethod6的代码将通过myValue参数指针或引用正确地获得新分配内存的内存地址。


没有指向引用的指针。


引用是远离指针的抽象。推荐信有点难搞砸,特别是对于新手来说,而且有点高。

你不需要证明人。你总是可以使用指针。但是,有时代码可以更容易地与它们一起阅读。

典型的初学者示例是链表。假设您有一个名为"list"的变量,其中包含指向第一个变量的指针。如果你想向头部添加一些东西,你需要给a d d()一个双指针,因为它需要能够修改"head"。但是,您可以使用对指针的引用。这里,我们希望在列表中使用指针,因为我们将改变它们,但是如果我们将引用传递给列表的头部而不是双指针,那么add()函数将更清晰。

它们只是一种风格选择。如果你在做一个更大的项目,你应该选择项目的风格。如果没有,你可以用你觉得更好的东西。然而,如果你希望成为一个成功的C++程序员,你应该使用所有的风格。

同样值得注意的是,您不能有指向引用的指针。这是因为引用实际上只是另一个变量的另一个名称,它可能在其他范围内。有一个指向引用的指针是没有意义的。你真正想要的只是一个指向原始数据的指针,不涉及引用。


需要注意的是,尽管引用不是对象,因此没有可访问的地址,但引用可以包含在对象中,并且包含对象确实有地址。

1
2
3
4
5
struct contains_ref
{
     int& ref;
     contains_ref(int& target) : ref(target) {}
};

"引用是别名"的解释是不正确的,但往往伴随着误导性的声明。引用不等于原始对象。它有自己的生存期,由包含它的作用域或对象决定,而不是它所引用的对象。并且一个引用可以比一个对象长寿,并且用于引用在同一地址创建的新对象。

将一个引用视为它真正的样子——一个指针周围的抽象,它将空值排除为一个有效值,并防止重新密封1——而不是什么魔术。引用的唯一不寻常的属性不是从其指针性质派生的,而是临时的生命期扩展。

实际上,这是因为C++没有提供任何引用引用本身的语法,而不是它的目标。所有的操作符,包括赋值操作符,都简单地应用于目标。


试着自己看看每件事都是什么。示例程序只打印int的值和不同实体的地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>

int main(){
  int myInt ;
  int *ptr_to_myInt = &myInt;
  int *ptr_to_myInt_ref = ptr_to_myInt;

  myInt = 42;

  printf("myInt is %d
"
,myInt);
  printf("ptr_to_myInt is %x
"
,ptr_to_myInt);
  printf("ptr_to_myInt_ref is %x
"
,ptr_to_myInt_ref);
  printf("&ptr_to_myInt is %x
"
,&ptr_to_myInt);

  return 0;
}

输出:

1
2
3
4
myInt is 42
ptr_to_myInt is bffff858
ptr_to_myInt_ref is bffff858
&ptr_to_myInt is bffff854

所以,指向int的指针和指向int引用的指针是完全相同的。这在代码中很明显,因为指向引用的指针只是给指针加别名的另一种方式(它表示"为我保留以下地址")。

现在,指针在内存中还需要一些空间,如果打印对该指针的引用(最后一条printf语句),它只指示指针在内存中的位置。