引用的本质
本篇文章我们深入探讨下引用存在的价值和引用的本质,先来看看引用和指针的两个经典案例,交换两个变量:
//通过传入变量地址达到交换的目的
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
//传入变量的别名相当于是本身达到交换的目的
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 10;
int b = 20;
swap(a, b);
cout << "a is " << a << endl;
cout << "b is " << b << endl;
return 0;
}
引用是C++的特性,C没有,通过上述列子以及通过种种迹象表明跟指针有某种联系,下面我们先来看看引用占用的内存大小。
int age = 10;
int &rage = age;
cout << sizeof(rage) << endl; //4
cout << sizeof(age) << endl; //4
这样验证似乎看不出端倪,都是输出4,因为引用是变量的别名,这两个sizeof只跟变量有关,没关系我们将引用放到结构体里面,然后用sizeof结构体对象来验证,定义一个包含引用的结构体:
struct Person {
int age;
const int &heigh = 10; //引用必须初始化,这里就声明一个常引用
};
然后我们输出下这个结构体大小:
Person p = {10};
cout << sizeof(p) << endl; //16
结构体大小竟然为16,int占四个字节,引用不可能占12个字节,而根据内存对齐原则,引用占用8个字节,对齐后才是16个字节,8个字节跟指针占用字节大小一样。不禁让人想看看指针和引用的汇编代码到底怎么处理的?接下来我们通过断点查看反汇编代码:
//指针
int age = 18;
int *p = &age;
*p = 20;
//以上代码转为反汇编代码如下:
pushq %rbp
movq %rsp, %rbp
xorl %eax, %eax
movl $0x0, -0x4(%rbp)
movl $0x12, -0x8(%rbp)//将rbp-0x8地址此时的值赋值为18,占四个字节
leaq -0x8(%rbp), %rcx //将rbp-0x8地址赋值给rcx,占8个字节
movq %rcx, -0x10(%rbp) //这里将rcx赋值到rbp-0x10地址中,占8个字节
movq -0x10(%rbp), %rcx //将rbp-0x10地址又赋值到rcx,占8个字节
movl $0x14, (%rcx) //将0x14也就是20赋值到rcx指向的地址中,占4个字节
popq %rbp
retq
以上是指针修改age,并且它的反汇编代码,对部分汇编代码进行了解释和说明,我们再来看看引用的代码:
//引用
int age = 18;
int &rAge = age;
rAge = 20;
//通过断点转为反汇编代码如下:
pushq %rbp
movq %rsp, %rbp
xorl %eax, %eax
movl $0x0, -0x4(%rbp)
movl $0x12, -0x8(%rbp)
leaq -0x8(%rbp), %rcx
movq %rcx, -0x10(%rbp)
movq -0x10(%rbp), %rcx
movl $0x14, (%rcx)
popq %rbp
retq
通过以上反汇编带的阅读和对比,结果发现它们的汇编代码一模一样,目前为止,可以得出一个结论:引用的本质就是指针,引用是弱化了的指针,它比指针更安全。指针地址如果不是const修饰是可以修改指向的内存,而引用只能指向开始初始化的变量的地址,不能修改。相当于被const修饰的指针一样。
int age = 10;
int *page = &age;
int &rage = age; //相当于int* const page = &age;
引用在C++中十分常用,通过对引用大量的学习,以及通过反汇编对其本质研究,希望同学们学得愉快,学得爽,如有任何疑问,欢迎留言,祝进步!
网友评论