指针、引用和指向指针的指针在C++中有什么区别?
在什么情况下应该优先选择其中的一种?
首先,指向指针的引用就像引用任何其他变量一样:
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++中,指向引用的指针是非法的,因为与指针不同,引用只是一个概念,允许程序员创建其他东西的别名。指针是内存中的位置,保存着其他东西的地址,但引用却不是。
现在,如果你坚持将引用视作指针来处理,可能最后一点不太清楚,比如:
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
)。
它会以类似于这样的方式存储在内存中(我特意简化了这个例子):
Memory address Value
0x12345678 0x0000004E
如果您想创建一个指向这个值的“指针”,它在内存中的表示如下:
Memory address Value
0x22334455 0x12345678
现在你有一个指针,其内存地址为0x22334455
,其值为0x12345678
,也就是用户输入的整数值(0x4E
)所存储的内存地址。
假设你想创建一个指向该指针值的“指针”,看起来会像这样:
Memory address Value
0x11335577 0x22334455
现在你在内存中有一个新的“指针”值,它存储着先前定义的指针值的内存地址。
指针可以无限制地这样创建——关键是记住指针只是另一个编译器将其解释为内存位置的值(它提供了各种访问语义,例如特殊于“指针”类型的*
和->
)。
引用可以被看作是对另一个真实对象的视图或别名。当你创建一个指向指针的引用(称为myReference
)时,你只是定义了一个可以用来访问你之前在内存中定义的指针的新名称myReference
。
在内部,引用使用指针实现,但这超出了你的问题所涉及的范围。
与C++中的其他类型相比,引用具有限制——例如,在创建引用时,必须始终初始化引用以“引用”实际对象,而指针可能指向无效或未初始化的内存。
这并不存在。正如前面所述,引用仅仅是另一个对象的别名。你不能“指向”引用,因为它本身不是一个对象,而只是另一个真实对象的名称。
当然,你可以有一个指向引用所引用对象的指针。但现在我们回到了普通指针领域。
当你将参数按值传递给方法或例程时,实际上是将对象的“副本”传递给了方法。在例程内部对该值进行的任何更改将在例程返回时丢失,因为该参数将被视为例程上下文中的局部变量。
如果要修改传入的参数以便客户端(调用)代码可以访问更改,必须通过指针
或引用
传递该参数。
例如:
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;
}
现在假设您的方法需要为指针分配内存。 您可能会尝试这样做:
void myMethod4(int* myValue)
{
// Warning: You will lose the address of the allocated
// memory when you return!
myValue = new int[5];
}
但要记住,这里修改的是指针值的副本,而不是真正的指针值。由于你希望在这个例程中修改指针,而不是指针“指向”的值,因此你需要将其作为“指向指针”的指针或“指向指针”的引用传递进去:
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];
}
在这两个示例中,调用 myMethod5
和 myMethod6
的代码将通过 myValue
参数指针或引用正确获取新分配内存的内存地址。指针不可能指向引用。
需要注意的是,尽管引用本身不是一个对象,因此没有可访问的地址,但引用可以包含在对象内部,而容纳引用的对象确实具有地址。
struct contains_ref
{
int& ref;
contains_ref(int& target) : ref(target) {}
};
试着自己看看每个东西都是什么。示例程序只是打印int的值和不同实体的地址:
#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\n",myInt);
printf("ptr_to_myInt is %x\n",ptr_to_myInt);
printf("ptr_to_myInt_ref is %x\n",ptr_to_myInt_ref);
printf("&ptr_to_myInt is %x\n",&ptr_to_myInt);
return 0;
}
输出:
myInt is 42
ptr_to_myInt is bffff858
ptr_to_myInt_ref is bffff858
&ptr_to_myInt is bffff854
因此,指向int的指针和指向int引用的指针实际上是完全相同的东西。从代码中可以看出这一点,因为指向引用的指针只是另一种给指针取别名的方式(它表示“为我保存以下地址”)。
现在,指针也需要一些内存空间,如果您打印该指针的引用(最后一个printf语句),它只是指示指针所在的内存位置。
c++
而不是c
!此外,“引用”与内存地址是不同的主题,它是在c++
中添加的。它们有些相关但并不相同。 - milad
rx
)来获取对象(x
)的指针,而不是引用的指针。 - Ben Voigt