编译器如何理解C++的指针和引用?

news/2024/7/5 2:09:06

        初学引用时,往往很难真正理解引用,它与指针究竟有什么区别和联系。下面我们不妨看看编译器如何理解引用和指针的。

一.函数通过指针传参

1.1 示例代码

#include <iostream>

using namespace std;

void swap(int *x,int *y)//指针传参 
{
	int tmp;
	tmp = *x;
	*x = *y;
	*y = tmp;
}

int main(int argc, char** argv) 
{
	int a = 20;
	int b = 30;
	
	swap(&a,&b);//指针传参 
	
	return 0;
}

1.2 汇编代码

1.2.1 main函数汇编代码

<+0>:	push   %rbp
<+1>:	mov    %rsp,%rbp
<+4>:	sub    $0x30,%rsp
<+8>:	mov    %ecx,0x10(%rbp)
<+11>:	mov    %rdx,0x18(%rbp)
<+15>:	callq  0x40e780 <__main>
<+20>:	movl   $0x14,-0x4(%rbp)//栈中偏移4的空间初始化为20,即int a = 20
<+27>:	movl   $0x1e,-0x8(%rbp)//栈中偏移8的空间初始化为30,即int b = 30
<+34>:	lea    -0x8(%rbp),%rdx//取b的地址传给rdx
<+38>:	lea    -0x4(%rbp),%rax//取a的地址传给rax
<+42>:	mov    %rax,%rcx//rax的值传给rcx,即a的地址保存到rcx中
<+45>:	callq  0x401530 <swap(int*, int*)>//引用传参的函数
<+50>:	mov    $0x0,%eax
<+55>:	add    $0x30,%rsp
<+59>:	pop    %rbp
<+60>:	retq   

 由上图可知:

(1)main函数在栈中开辟了两个4字节空间,分别分配给a和b,并且分别初始化为20和30

<+20>:	movl   $0x14,-0x4(%rbp)//栈中偏移4的空间初始化为20,即int a = 20
<+27>:	movl   $0x1e,-0x8(%rbp)//栈中偏移8的空间初始化为30,即int b = 30

(2)准备传给swap函数的参数

<+34>:	lea    -0x8(%rbp),%rdx//取b的地址传给rdx
<+38>:	lea    -0x4(%rbp),%rax//取a的地址传给rax
<+42>:	mov    %rax,%rcx //rax的值传给rcx,即a的地址保存到rcx中
<+45>:	callq  0x401530 <swap(int&, int&)>//引用传参的函数

        swap函数的参数分别保存在rcx和rdx中,且是参数地址。

1.2.2 swap函数汇编代码

<+0>:	push   %rbp
<+1>:	mov    %rsp,%rbp
<+4>:	sub    $0x10,%rsp
<+8>:	mov    %rcx,0x10(%rbp)//a的地址保存到栈中(对应形参x)
<+12>:	mov    %rdx,0x18(%rbp)//b的地址保存到栈中(对应形参y)
<+16>:	mov    0x10(%rbp),%rax//取出a的地址到rax中
<+20>:	mov    (%rax),%eax//a的值赋给eax(4字节)
<+22>:	mov    %eax,-0x4(%rbp)//eax的值赋给栈偏移为4的变量tmp中,即tmp = a
<+25>:	mov    0x18(%rbp),%rax//取出b的地址到rax中
<+29>:	mov    (%rax),%edx//b的值赋给edx(4字节)
<+31>:	mov    0x10(%rbp),%rax//取出a的地址到rax中
<+35>:	mov    %edx,(%rax)//eax的值赋给a,即a = b
<+37>:	mov    0x18(%rbp),%rax//取出b的地址到rax中
<+41>:	mov    -0x4(%rbp),%edx//edx = tmp
<+44>:	mov    %edx,(%rax) //b = edx,即b = tmp
<+46>:	add    $0x10,%rsp
<+50>:	pop    %rbp
<+51>:	retq  

        由上图可知:

(1)swap函数接收了a和b变量的地址

<+8>:	mov    %rcx,0x10(%rbp)//a的地址保存到栈中(对应形参x)
<+12>:	mov    %rdx,0x18(%rbp)//b的地址保存到栈中(对应形参y)

(2)直接交换a、b空间的值

<+16>:	mov    0x10(%rbp),%rax//取出a的地址到rax中
<+20>:	mov    (%rax),%eax//a的值赋给eax(4字节)
<+22>:	mov    %eax,-0x4(%rbp)//eax的值赋给栈偏移为4的变量tmp中,即tmp = a
<+25>:	mov    0x18(%rbp),%rax//取出b的地址到rax中
<+29>:	mov    (%rax),%edx//b的值赋给edx(4字节)
<+31>:	mov    0x10(%rbp),%rax//取出a的地址到rax中
<+35>:	mov    %edx,(%rax)//eax的值赋给a,即a = b
<+37>:	mov    0x18(%rbp),%rax//取出b的地址到rax中
<+41>:	mov    -0x4(%rbp),%edx//edx = tmp
<+44>:	mov    %edx,(%rax) //b = edx,即b = tmp

二.函数通过引用传参

2.1 示例代码

#include <iostream>

using namespace std;

void swap(int &x,int &y)//引用传参 
{
	int tmp;
	tmp = x;
	x = y;
	y = tmp;
}

int main(int argc, char** argv) 
{
	int a = 20;
	int b = 30;
	
	swap(a,b);//引用传参 
	
	return 0;
}

2.2 汇编代码

2.2.1 main函数汇编代码

<+0>:	push   %rbp
<+1>:	mov    %rsp,%rbp
<+4>:	sub    $0x30,%rsp
<+8>:	mov    %ecx,0x10(%rbp)
<+11>:	mov    %rdx,0x18(%rbp)
<+15>:	callq  0x40e780 <__main>
<+20>:	movl   $0x14,-0x4(%rbp)//栈中偏移4的空间初始化为20,即int a = 20
<+27>:	movl   $0x1e,-0x8(%rbp)//栈中偏移8的空间初始化为30,即int b = 30
<+34>:	lea    -0x8(%rbp),%rdx//取b的地址传给rdx
<+38>:	lea    -0x4(%rbp),%rax//取a的地址传给rax
<+42>:	mov    %rax,%rcx //rax的值传给rcx,即a的地址保存到rcx中
<+45>:	callq  0x401530 <swap(int&, int&)>//引用传参的函数
<+50>:	mov    $0x0,%eax
<+55>:	add    $0x30,%rsp
<+59>:	pop    %rbp
<+60>:	retq   

        由上图可知:

(1)main函数在栈中开辟了两个4字节空间,分别分配给a和b,并且分别初始化为20和30

<+20>:	movl   $0x14,-0x4(%rbp)//栈中偏移4的空间初始化为20,即int a = 20
<+27>:	movl   $0x1e,-0x8(%rbp)//栈中偏移8的空间初始化为30,即int b = 30

(2)准备传给swap函数的参数

<+34>:	lea    -0x8(%rbp),%rdx//取b的地址传给rdx
<+38>:	lea    -0x4(%rbp),%rax//取a的地址传给rax
<+42>:	mov    %rax,%rcx //rax的值传给rcx,即a的地址保存到rcx中
<+45>:	callq  0x401530 <swap(int&, int&)>//引用传参的函数

        swap函数的参数分别保存在rcx和rdx中,且是参数地址。

2.2.2 swap函数汇编代码

<+0>:	push   %rbp
<+1>:	mov    %rsp,%rbp
<+4>:	sub    $0x10,%rsp
<+8>:	mov    %rcx,0x10(%rbp)//a的地址保存到栈中(对应形参x)
<+12>:	mov    %rdx,0x18(%rbp)//b的地址保存到栈中(对应形参y)
<+16>:	mov    0x10(%rbp),%rax//取出a的地址到rax中
<+20>:	mov    (%rax),%eax//a的值赋给eax(4字节)
<+22>:	mov    %eax,-0x4(%rbp)//eax的值赋给栈偏移为4的变量tmp中,即tmp = a
<+25>:	mov    0x18(%rbp),%rax//取出b的地址到rax中
<+29>:	mov    (%rax),%edx//b的值赋给edx(4字节)
<+31>:	mov    0x10(%rbp),%rax//取出a的地址到rax中
<+35>:	mov    %edx,(%rax)//eax的值赋给a,即a = b
<+37>:	mov    0x18(%rbp),%rax//取出b的地址到rax中
<+41>:	mov    -0x4(%rbp),%edx//edx = tmp
<+44>:	mov    %edx,(%rax) //b = edx,即b = tmp
<+46>:	add    $0x10,%rsp
<+50>:	pop    %rbp
<+51>:	retq   

 由上图可知:

(1)swap函数接收了a和b变量的地址

<+8>:	mov    %rcx,0x10(%rbp)//a的地址保存到栈中(对应形参x)
<+12>:	mov    %rdx,0x18(%rbp)//b的地址保存到栈中(对应形参y)

(2)直接交换a、b空间的值

<+16>:	mov    0x10(%rbp),%rax//取出a的地址到rax中
<+20>:	mov    (%rax),%eax//a的值赋给eax(4字节)
<+22>:	mov    %eax,-0x4(%rbp)//eax的值赋给栈偏移为4的变量tmp中,即tmp = a
<+25>:	mov    0x18(%rbp),%rax//取出b的地址到rax中
<+29>:	mov    (%rax),%edx//b的值赋给edx(4字节)
<+31>:	mov    0x10(%rbp),%rax//取出a的地址到rax中
<+35>:	mov    %edx,(%rax)//eax的值赋给a,即a = b
<+37>:	mov    0x18(%rbp),%rax//取出b的地址到rax中
<+41>:	mov    -0x4(%rbp),%edx//edx = tmp
<+44>:	mov    %edx,(%rax) //b = edx,即b = tmp

三.汇编对比

3.1 main函数对比

        如下图所示。除了swap函数的形式不同,其他完全一样。

579bc67d781b4e7fad95a3e767616c9a.png

 

3.2 swap函数对比

        如下图所示。它们的代码完全一样。

6e5782191b8e4a428cf4eeb31c8514a4.png

四.结论

(1)引用传参和指针传参的汇编实现是一样的。

(2)引用也是传递变量指针给swap函数。

(3)引用只是C++语言的语法糖,它本质上还是指针。

 


http://lihuaxi.xjx100.cn/news/2160550.html

相关文章

网络安全---非对称数据加密签名验证

一、课题描述 三位同学一组完成数据的非对称加密和数字签名验证传输。 三位同学分别扮演图中 Alice、Bob 和 CA 三个角色&#xff0c;Bob 和 Alice 从 CA 中获得数字证书、Bob 向 Alice 发送秘密发送一段加密并签名后的信息&#xff0c;Alice 获取 Bob 发送的加密信息&#x…

操作系统的基础知识:操作系统的特征:并发,共享,虚拟,异步

操作系统的特性&#xff1a; 1.并发 并发:指两个或多个事件在同一时间间隔内发生。这些事件宏观上是同时发生的&#xff0c;但微观上是交替注意&#xff1a;并行:指两个或多个事件在同一时刻同时发生。 操作系统的并发性指计算机系统中“同时”运行着多个程序&#xff0c;这…

具身智能潮起、巨头环伺之下,优必选如何撑起资本期待?

AI大模型风口之下&#xff0c;作为“具身智能”最佳载体&#xff0c;人形机器人似乎即将迎来“觉醒元年”。 前有马斯克巨大影响力加持之下的人形机器人Optimus亮相&#xff0c;后有OpenAI联合人形机器人初创公司Figure推出令人惊艳的Figure 01。可以看出&#xff0c;AI软件和…

AcWing---游戏---区间dp

1388. 游戏 - AcWing题库 思路&#xff1a; 两个人比赛&#xff0c;是一道博弈论问题&#xff0c;主要思想就是A-B取到最大值。A是我方得到的分数&#xff0c;B是对方得到的分数。我们设g[i][j]是从第i个数到第j个数&#xff0c;先手-后手取得的最大值&#xff0c;分类讨论&a…

azkaban的写法

先创建一个.job文件和一个.sql文件 sql语法写到一个test名字的文件里&#xff0c;之后job写法如下&#xff1a; typecommand commandhive -f test6.sql 一定要严格写&#xff0c;不管是字母还是空格&#xff0c;单引号中就是sql文件的名字 然后将它们一块打包&#xff0c;启动…

【漏洞复现】WordPress Welcart 任意文件读取漏洞(CVE-2022-4140)

0x01 产品简介 Welcart 是一款免费的 WordPress 电子商务插件。Welcart 具有许多用于制作在线商店的功能和自定义设置。您可以轻松创建自己的原始在线商店。 0x02 漏洞概述 Welcart存在任意文件读取漏洞&#xff0c;未授权的攻击者可以通过该漏洞读取任意文件&#xff0c;获…

ETLCloud结合kafka的数据集成

一、ETLCloud中实时数据集成的使用 在ETLCloud中数据集成有两种方式&#xff0c;一种是离线数据集成&#xff0c;另一种便是我们今天所要介绍的实时数据集成了&#xff0c;两者的区别从名字便可以得知&#xff0c;前者处理的数据是离线的没有时效性的&#xff0c;后者的数据是…

springcloud==springboot3.X+JDK21

2024年新版springcloud springboot3.X JDK21 ROADMAP 配套代码地址 GitHub - hebian1994/cloud2024