导读
- 为什么说引用是别名
- 为什么引用作为形参可以修改实参的值
- 为什么引用初始化后无法指向其他对象
- 引用与指针对应的汇编代码
- 对引用的理解
为什么说引用是别名
int main() {
int a = 10;
int &b = a;
b = 20;
return 0;
}
1-1
从地址的角度来说,a与b两者的内存地址相同,都存储着0x14这个数值,所以b可以理解为a的别名。既然引用是别名,显然需要通过某个变量初始化,因此引用必须在定义
的时候直接
通过某个变量
初始化。
为什么引用作为形参可以修改实参的值
void test(int &x) {
x = 99;
}
int main() {
int a = 10;
test(a);
return 0;
}
2-1
2-2
在调用函数前,将a的地址存入rdi中,此时可以通过rdi存储的地址值来对变量a进行读写操作。
2-3进入函数后,向rdi存储的内存地址中写入0x63数据,即十进制的99。由于rdi中存储的是变量a的内存地址,所以此时a的值等于99。
为什么引用初始化后无法指向其他对象
例子中的注释对应着汇编代码:
int main() {
// movl $0xa, -0x8(%rbp)
int a = 10;
// leaq -0x8(%rbp), %rcx
// movq %rcx, -0x10(%rbp)
// movq -0x10(%rbp), %rcx
int &x = a;
// movl $0x14, (%rcx)
x = 20;
// movl $0x63, -0x14(%rbp)
int b = 99;
return 0;
}
可以看到,在对引用赋值时,转成汇编对应的是通过寄存器赋值,中间通过movq %rcx, -0x10(%rbp)
转存了一下a的内存地址。那么,是否可以通过-0x10(%rbp)
这个内存地址来修改x的指向?
可见,当我们向-0x10(%rbp)
内存地址中写入变量b的内存地址时,引用x的值变成了变量b的值99。虽然可以通过lldb修改引用的指向,但在C++层面很难得知引用的内存地址(即使通过代码中变量存储地址推导出引用地址,同样需要直接操作内存地址,因为没有一个变量名代表这个内存地址)。
当对引用使用&
符号时,得到的是变量a的地址,而非引用的地址(请参考为什么说引用是别名
这一小节)。
是否可以通过直接修改&引用
的值,来达到改变引用指向?
很遗憾,&x
无法赋值,因为&x
等于变量a的地址,而a的地址是个常量,常量不能是左值。
引用与指针对应的汇编代码
int main() {
// movl $0xa, -0x8(%rbp)
int a = 10;
// leaq -0x8(%rbp), %rcx
// movq %rcx, -0x10(%rbp)
int &x = a;
// movq %rcx, -0x18(%rbp)
int *p = &a;
return 0;
}
从汇编代码来看,无法区分是在操作引用还是在操作指针,两者的区别仅在于可以通过&p
获取指针的地址,从而改变指针变量p的指向,但是无法通过&x
获取引用的地址(获取到的是a的地址)。
对引用的理解
文章开头已经说过,可以把引用理解为变量的别名。但是通过为什么引用初始化后无法指向其他对象
这一小节可以看到,引用其实也是需要开辟内存的,只是内存地址没有对应的变量名,在这个内存地址中存储着引用的变量的内存地址,可以把这块因为引用而开辟的匿名内存地址理解为二级指针。
此处可以类比指针,指针存储着某个变量的地址,但是同样有一块空间存储着指针,而这块存储着指针的内存空间就是&指针
,即指针的地址,引用其实就是个匿名指针。
Have fun!
网友评论