引用的详解
引用的汇编底层角度
如上图所示在VS2022中反汇编的汇编语言看出来。源代码int &b = a和int* const c = &a的汇编代码是一模一样的,都是把 a 变量的地址放入eax寄存器中,然后在赋值给变量b和c。这很明显是个指针行为也证明了引用是指针常量实现的,也可以把引用当作指针常量来理解。
既然引用是指针常量实现,那么这个"别名"变量也是有内存空间占用的,且占用大小同指针大小一样。
可是我们sizeof运算别名只能得到原名对象数据类型大小,这是因为sizeof对引用的特殊处理了。
既然"别名"也是变量,那肯定也有内存地址,可是我们对别名取地址时,只能得到原名对象数据的地址,这是因为编译器改变了引用的取地址含义,这样想要获得"别名"变量的地址变得很困难,但是我们记住"别名"变量有地址,大小和指针一样
指针行为
class Person { public: int age; };
Person p1;
Person *ptr1 = &p1;
cout << ptr1->age << endl;//输出无规则的脏数据,因为没有初始化。。。等价于(*ptr).age
- 为什么指针用
->
?因为这是一个指针行为,具体汇编角度来看,会把指针指向的地址放入寄存器,再从寄存器读出地址去访问。
此时再加上以下三行代码
Person& pp = p1;
cout<<ptr1->age<<endl;//输出0
cout<<pp.age<<endl;//输出0
- 为什么引用别名的pp要用
.
呢?明明引用的底层本质也是指针行为。具体来说,我觉得无论是->
,.
都是指针行为访问数据。具体情况为ptr指针变量的变量本身的地址与指向的p1是非连续的。而后者pp别名因为本身的变量地址被隐藏而形成了"别名"的特性,使得和原名p1假装连续了。 - 而之所以前面的age输出脏数据,而加上别名就总输出0。是因为这是编译器看到别名而带动的初始化。(不重要,要有不使用未初始化数据的好习惯)
指针和引用的区别
指针简单来说是一个变量存储一个地址,然后根据指针类型的那个类型如int来按照int类型规则指向那块内存。
引用简单来说就是对一个其他的变量(原名变量)起了个别名。对别名的操作本质上都是对原名变量的操作。
引用的本质就是指针
具体区别如下
- 二者都是变量,从变量的角度出发是有占用内存的地址和占用内存大小的。都是跟随系统架构位数决定的,也就是内存寻址能力。如x86架构的大小都是4B能寻址4GB内存,指针内存地址也是可以通过&取值符号求得。但是,引用的这个"别名变量"很难求得占用内存的地址和占用内存大小,都被编译器优化了。上述详解有说明
- 既然指针变量的地址可以求得,也就说可以用另外的指针变量存储嵌套形成多级指针,但是,因为没办法,只有一级
- 指针的这个变量值可以改变,意味着指向可以修改,但是,引用底层是指针常量实现的,无法改变指向。