指针变量和引用变量之间的区别是什么?


当前回答

引用不是给某些内存的另一个名称。它是一个不可变的指针,在使用时会自动取消引用。基本上归结为:

int& j = i;

它内部变成

int* const j = &i;

其他回答

简单地说,我们可以说引用是变量的替代名称,指针是保存另一个变量地址的变量。例如

int a = 20;
int &r = a;
r = 40;  /* now the value of a is changed to 40 */

int b =20;
int *ptr;
ptr = &b;  /*assigns address of b to ptr not the value */

为了避免混淆,我想输入一些输入,我确信这主要取决于编译器如何实现引用,但在gcc的情况下,引用只能指向堆栈上的变量的想法实际上并不正确,例如:

#include <iostream>
int main(int argc, char** argv) {
    // Create a string on the heap
    std::string *str_ptr = new std::string("THIS IS A STRING");
    // Dereference the string on the heap, and assign it to the reference
    std::string &str_ref = *str_ptr;
    // Not even a compiler warning! At least with gcc
    // Now lets try to print it's value!
    std::cout << str_ref << std::endl;
    // It works! Now lets print and compare actual memory addresses
    std::cout << str_ptr << " : " << &str_ref << std::endl;
    // Exactly the same, now remember to free the memory on the heap
    delete str_ptr;
}

其输出如下:

THIS IS A STRING
0xbb2070 : 0xbb2070

如果您注意到甚至内存地址都完全相同,这意味着引用成功地指向了堆上的一个变量!现在,如果你真的想变得古怪,这也很有效:

int main(int argc, char** argv) {
    // In the actual new declaration let immediately de-reference and assign it to the reference
    std::string &str_ref = *(new std::string("THIS IS A STRING"));
    // Once again, it works! (at least in gcc)
    std::cout << str_ref;
    // Once again it prints fine, however we have no pointer to the heap allocation, right? So how do we free the space we just ignorantly created?
    delete &str_ref;
    /*And, it works, because we are taking the memory address that the reference is
    storing, and deleting it, which is all a pointer is doing, just we have to specify
    the address with '&' whereas a pointer does that implicitly, this is sort of like
    calling delete &(*str_ptr); (which also compiles and runs fine).*/
}

其输出如下:

THIS IS A STRING

因此,引用是引擎盖下的指针,它们都只是存储一个内存地址,地址指向的位置是不相关的,如果我调用std::cout<<str_ref;调用delete str_ref后?很明显,它编译得很好,但在运行时会导致分段错误,因为它不再指向有效变量,我们本质上有一个中断的引用仍然存在(直到它超出范围),但没有用。

换句话说,引用只是一个指针,它抽象了指针机制,使其更安全、更容易使用(没有意外的指针数学,没有混淆“.”和“->”等),假设您没有像上面的例子那样尝试任何废话;)

现在,不管编译器如何处理引用,它总是有某种指针,因为引用必须引用特定内存地址处的特定变量,才能按预期工作,因此无法绕过这一点(因此称为“引用”)。

对于引用,唯一需要记住的重要规则是必须在声明时定义它们(头中的引用除外,在这种情况下,必须在构造函数中定义引用,在构造包含引用的对象之后,再定义它就太晚了)。

请记住,我上面的例子只是说明引用是什么的例子,你永远不想以这些方式使用引用!为了正确使用参考文献,这里已经有很多答案,这些答案一针见血

另一个区别是,可以有指向void类型的指针(这意味着指向任何对象的指针),但禁止引用void。

int a;
void * p = &a; // ok
void & p = a;  //  forbidden

我不能说我真的很满意这种特殊的差异。我更希望它能被允许有意义地引用任何有地址的东西,否则引用行为相同。它将允许使用引用定义一些C库函数的等价物,如memcpy。

“我知道引用是语法糖,所以代码更容易读写”

这引用不是实现指针的另一种方式,尽管它涵盖了大量的指针用例。指针是一种数据类型——通常指向实际值的地址。然而,它可以设置为零,或者使用地址算术等设置在地址之后的几个位置。对于具有自己值的变量,引用是“语法糖”。

C只有传递值语义。获取变量引用的数据的地址并将其发送到函数是一种通过“引用”传递的方法。引用通过“引用”原始数据位置本身在语义上简化了这一过程。因此:

int x = 1;
int *y = &x;
int &z = x;

Y是一个int指针,指向存储x的位置。X和Z表示相同的存储位置(堆栈或堆)。

很多人谈论过这两个(指针和引用)之间的区别,好像它们是同一个东西,用法不同一样。它们完全不同。

1) “指针可以被重新分配任意次数,而引用在绑定后不能被重新分配。”--指针是指向数据的地址数据类型。引用是数据的另一个名称。因此,您可以“重新分配”引用。你不能重新分配它所指的数据位置。就像你不能更改“x”所指的位置一样,你也不能更改“z”。

x = 2;
*y = 2;
z = 2;

相同的。这是一次重新分配。

2) “指针不能指向任何地方(NULL),而引用总是指向对象”——同样令人困惑。引用只是对象的另一个名称。空指针表示(语义上)它没有引用任何内容,而引用是通过表示它是“x”的另一个名称来创建的。自从

3) “你不能像用指针那样获取引用的地址”——是的,你可以。再次带着困惑。如果您试图查找用作引用的指针的地址,这是一个问题——因为引用不是指向对象的指针。他们就是目标。所以你可以得到对象的地址,也可以得到指针的地址。因为它们都在获取数据的地址(一个是对象在内存中的位置,另一个是指向对象在内存位置的指针)。

int *yz = &z; -- legal
int **yy = &y; -- legal

int *yx = &x; -- legal; notice how this looks like the z example.  x and z are equivalent.

4) “这里没有“引用算术”——同样有点混淆——因为上面的例子中z是对x的引用,因此两者都是整数,所以“引用”算术意味着例如将x引用的值加1。

x++;
z++;

*y++;  // what people assume is happening behind the scenes, but isn't. it would produce the same results in this example.
*(y++);  // this one adds to the pointer, and then dereferences it.  It makes sense that a pointer datatype (an address) can be incremented.  Just like an int can be incremented. 

指针是保存另一个变量的内存地址的变量,其中引用是现有变量的别名。(已存在变量的另一个名称)

1.指针可以初始化为:

int b = 15;
int *q = &b;

OR

int *q;
q = &b;

其中作为参考,

int b=15;
int &c=b;

(在一个步骤中声明和初始化)

指针可以分配给null,但引用不能可以对指针执行各种算术运算,而没有所谓的参考算术。指针可以重新分配,但引用不能指针在堆栈上有自己的内存地址和大小,而引用共享相同的内存地址