前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >引用的条件及从汇编角度理解引用

引用的条件及从汇编角度理解引用

作者头像
lexingsen
发布2022-02-24 15:42:48
4880
发布2022-02-24 15:42:48
举报
文章被收录于专栏:乐行僧的博客乐行僧的博客

引用:引用可以看作是对已定义变量的别名,变量名实际上是对一段连续存储空间的别名。

关于引用几点比较重要的内容:

(1)定义引用时必须进行初始化。 (2)初始化的值要能取地址,不能用一个立即数进行初始化。

代码语言:javascript
复制
int &p = 100;//这是错误的

(3)引用不能改变,一旦初始化,不能引用其他变量名。 (4)访问引用变量,永远访问的是被引用变量的内存。

引用的这几点重要的内容,可以反应出引用相对于指针来讲,更加安全。她不会引用一个未初始化的内存块,建议在C++中更多使用引用。 提出一个问题,引用究竟有没有进行内存的开辟?许多书籍上写出引用没有开辟空间,到底对不对呢,还是理解的方向不正确? 下面通过一段代码分析

代码语言:javascript
复制
#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的值。

代码语言:javascript
复制
#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的地址、引用的地址相同。似乎验证了刚才提到的问题,引用没有开辟独立的内存块。实际则不然,下面看上述代码的反汇编:

代码语言:javascript
复制
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。

代码语言:javascript
复制
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的地址,因为每当你使用引用时,已经经过解引用。

初学者如何方便的定义引用变量呢? 通过上边的分析,引用底层也是一个指针。只是在使用时,就进行了解引用,对程序员来讲这个过程是透明的。

代码语言:javascript
复制
int a=10;
int *p = &a;//首先定义一个指针
//将右边的取地址符&覆盖左边的*符号,即可得到引用变量的定义
int &p = a;

定义引用变量引用数组名

代码语言:javascript
复制
//按照上边的规则
int arr[10] = {0};
//首先定义一个指向数组的指针
int (*p)[10] = &arr;
//将右边的取地址符&覆盖左边的*符号,即可得到引用变量的定义
int (&p)[10] = arr;
代码语言:javascript
复制
#include<iostream>
using namespace std;
int main(){
    int arr[10] = {0};
    int (*p)[10] = &arr;
    int (&q)[10] = arr;
    cout<<sizeof(arr)<<" "
        <<sizeof(p)<<" "
        <<sizeof(q)<<endl;
    return 0;
}
这里写图片描述
这里写图片描述

64bit编译器下,指针变量的大小为8个字节。

引用变量作为函数参数 当数组名作为函数参数时会退化为指针,因此实际应用中往往还需要传递数组的长度。

代码语言:javascript
复制
void fun(int arr[]){
    printf("sizeof(arr)=%d\n",sizeof(arr));
}
int main(){
    int arr[10]={0};
    printf("sizeof(arr)=%d\n",sizeof(arr));
    fun(arr);
}

注:64bit编译器指针变量的大小为8个字节。

这里写图片描述
这里写图片描述

引用作为函数参数

代码语言:javascript
复制
void fun(int (&p)[10]){
    cout<<"sizeof(p)="<<sizeof(p)<<endl;
}
int main(){
    int arr[10]={0};
    int (&p)[10] = arr;
    cout<<"sizeof(arr)="<<sizeof(arr)<<endl;
    fun(p);
    return 0;
}
这里写图片描述
这里写图片描述

可见当引用变量引用数组名时,作为函数参数不会退化为指针。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档