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

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

一.函数通过指针传参

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++语言的语法糖,它本质上还是指针。

 

  • 31
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈工程师修炼日记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值