引用:引用可以看作是对已定义变量的别名,变量名实际上是对一段连续存储空间的别名。
关于引用几点比较重要的内容:
(1)定义引用时必须进行初始化。
(2)初始化的值要能取地址,不能用一个立即数进行初始化。
(3)引用不能改变,一旦初始化,不能引用其他变量名。
(4)访问引用变量,永远访问的是被引用变量的内存。
提出一个问题,引用究竟有没有进行内存的开辟?许多书籍上写出引用没有开辟空间,到底对不对呢,还是理解的方向不正确?
下面通过一段代码分析
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int &b = a;
int *p = &a;
b = 20;
cout <<"a = "<<a<<",b = "<<b<<",*p = "<<*p<<endl;
*p = 30;
cout <<"a = "<<a<<",b = "<<b<<",*p = "<<*p<<endl;
return 0;
}
由上图看出,修改引用和修改指针都达到修改a内存块值的目的。也就是说 a b *p
似乎是一块内存。
试着输出一下int a = 10;int &b = a;int *p = &a;
中a和b的地址,以及p的值。
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int &b = a;
int *p = &a;
cout << hex << "&a = " << &a << ",&b = " << &b << ",p = " << p << endl;
return 0;
}
在输出结果中,似乎变量a的地址、引用的地址相同。似乎验证了刚才提到的问题,引用没有开辟独立的内存块。实际则不然,下面看上述代码的反汇编:
1. int a = 10;
2. mov dword ptr [ebp-4],0Ah
3. int &b = a;
4. lea eax,[ebp-4]
5. mov dword ptr [ebp-8],eax
6. int *p = &a;
7. lea ecx,[ebp-4]
8. mov dword ptr [ebp-0Ch],ecx
对比上边3,4,5和6,7,8汇编代码,我们发现在底层语言中,压根就没有引用和指针的区别。
在函数栈帧的开辟中,用栈底指针ebp的偏移量表示局部变量的地址。[ebp-4]对应的内存块就是a。
int &b = a;
lea eax,[ebp-4]//就是将内存块a的地址保存在eax寄存器中,lea指令是移地址指令,对比下边int *p = &a的汇编指令是一摸一样.
dword ptr [ebp-8],eax//[ebp-8]即是引用b的内存块,所以说引用是开辟了内存块的,用来保存被引用变量的地址。
int *p = &a;
lea ecx,[ebp-4]//就是将内存块a的地址保存在eax寄存器中,lea指令是移地址指令
mov dword ptr [ebp-0Ch],ecx
通过上边的反汇编,我们可以得到的一条结论是:引用实际上开辟了内存用于保存被引用变量的地址。
但是,为什么我们输出引用变量b的地址,却是内存块a的地址呢?
实际上,只要一旦使用,在编译器内部就会自动进行解应用。也就是说永远不可能访问到引用变量b的地址,因为每当你使用引用时,已经经过解引用。