C++ 指针和引用的区别 引用底层的原理

以下内容转载自 https://www.toutiao.com/i6826722460080013837/ 

C++中指针与引用的区别

原创算法集市2020-05-15 07:30:00

初学C++时,很容易把指针和引用的用法混在一起,下面通过一些示例来说明指针和引用两者之间的差别。

1、两者的定义和性质不同

指针是一个变量,存储的是一个地址,指向内存的一个存储单元;

引用是原变量的一个别名,跟原来的变量实质上是同一个东西。

int a = 996;
int *p = &a; // p是指针, &在此是求地址运算
int &r = a; // r是引用, &在此起标识作用

上面定义了一个整型变量 a,p 是一个指针变量,p 的值是变量 a 的地址;

而引用 r,是 a 的一个别名,在内存中 r 和 a 占有同一个存储单元,如下图所示:

C++中指针与引用的区别

 

2、指针可以有多级,引用只能是一级

int **p; // 合法
int &&a; // 不合法

3、指针可以在定义的时候不初始化,引用必须在定义的时候初始化

int *p; 	// 合法
int &r; 	// 不合法
int a = 996;
int &r = a;	// 合法

4、指针可以指向NULL,引用不可以为NULL

int *p = NULL; 	// 合法
int &r = NULL; 	// 不合法

5、指针初始化之后可以再改变,引用不可以

int a = 996;
int *p = &a; // 初始化, p 是 a 的地址
int &r = a; // 初始化, r 是 a 的引用

int b = 885;
p = &b; // 合法, p 是 b 的地址
r = b; // 不合法, r 不可以再变

6、sizeof 的运算结果不同

int a = 996;
int *p = &a;
int &r = a;

cout << sizeof(p); // 返回 int* 类型的大小
cout << sizeof(r); // 返回 int 类型的大小

在64位机器上,int* 类型的大小为8个字节,int类型的大小为4个字节。

sizeof 是C/C++ 中的一个操作符(operator),其作用就是返回一个对象或者类型所占的内存字节数。

The sizeof keyword gives the amount of storage, in bytes, associated with a variable or a type(including aggregate types). This keyword returns a value of type size_t.

7、自增运算意义不同

如下图所示,p++之后指向a后面的内存,r++相当于a++。

C++中指针与引用的区别

 

8、指针和引用作为函数参数时,指针需要检查是否为空,引用不需要

void fun_p(int *p)
{
    // 需要检查P是否为空
    if (p == NULL) 
    {
        // do something
    }
}

void fun_r(int &r)
{
    // 不需要检查r
    // do something
}

PS:指针和引用都可以作为函数参数,改变实参的值。

 

 

以下内容转载自 https://blog.csdn.net/lws123253/article/details/80353197

文章目录


初学c++中的“引用”这一概念的时候,很多人都是懵的,大家大概都会产生这样的疑问?
什么是引用?
引用占用内存吗?

于是,为了验证你的猜想,你可能会写出下面这样的代码来验证:

#include<iostream>
using namespace std;
int main()
{
	int  a = 1;
	int&  b = a;
	cout << "a:address->" << &a << endl;
	cout << "b:address->" << &b << endl;
	
	getchar();
	return 0;
}

运行结果:
a:address->0031FD54
b:address->0031FD54

我们会发现,引用b的地址和变量a的地址一样。于是,有人猜想是不是说变量a和引用b本身就是一个东西。所以同样的,引用本身所占内存就是变量a的内存。

**首先对于这个说法,肯定是不正确的。**至于为什么不正确,我们接下来会以底层原理为大家解释。

##什么是引用

为了看看引用的底层究竟是怎样实现的,我决定写一个简短的代码,然后反汇编(vs编译环境在调试模式下,右键鼠标菜单->反汇编)看一看。

#include<iostream>
using namespace std;
int main()
{
int x=1;
int &b=x;
return 0;
}

我们再看下转汇编后的汇编代码:

9:       int x = 1; 	//源代码 
00401048   mov         dword ptr [ebp-4],1	//反汇编代码  
10:      int &b = x; 	//源代码
0040104F   lea         eax,[ebp-4]  		//反汇编代码
00401052   mov         dword ptr [ebp-8],eax//反汇编代码

在这里解释下这三行反汇编代码:
mov dword ptr [ebp-4],1 //把1赋值给ebp(栈底指针)-4的地址
lea eax,[ebp-4] //把ebp-4的地址赋值给寄存器eax
mov dword ptr [ebp-8],eax //把寄存器eax里的值赋值给ebp-8的这块地址
上述三行代码的作用就是将1赋值给x,然后将x的地址赋值给了引用b。
而在内存中,它是这样的:
这里写图片描述
注意:因为栈在内存中是由高地址向低地址增长的
通过底层的分析,我们不难理解引用的本质就是所引用对象的地址

建议:有兴趣的同学可以了解一下常见的汇编指令,对于了解代码底层原理有很大的帮助。
##引用占用内存吗
通过上面的分析,我们得出了引用本身存放的是引用对象的地址,通俗点理解就是引用就是通过指针来实现的,所以,应用所占的内存大小就是指针的大小。
##引用的地址
在最开始,我们写过一段代码来测试引用的地址,发现引用的地址和变量的地址是一样的。但是,在后面对引用的底层分析后发现,它本身又存放的是变量的地址,即引用的值是地址,那么这不是很冲突吗?

事实上, b的地址我们没法通过&b获得,因为编译器会将&b解释为:&(*b) =&x ,所以&b将得到&x。也验证了对所有的b的操作,和对x的操作等同。

那么问题来了,我们如何才能获得引用的地址呢?
我们看下面这段代码:

#include<stdio.h>
#include <iostream>  
using namespace std;
int main()  
{  
   int x = 1;  
   int y = 2;  
   int &b = x;  
   printf("&x=%x,&y=%x,&b=%x,b=%x\n",&x,&y,&y-1,*(&y-1));  
   return 0;
 }   

输出:
&x=12ff7c,&y=12ff78,&b=12ff74,b=12ff7c

不知道看到这里大家明白了没有,引用b的地址我们可以间接通过&y-1来得到b的地址,从而得到b的值:*(&y-1) 从结果可以知道,b的值即x的地址,从而可以知道,从地层实现来看,引用变量的确存放的是被引用对象的地址,只不过,对于高级程序员来说是透明的,编译器 屏蔽了引用和指针的差别。

说明:大家在实践的时候,这里的x和y的地址不一定是连续的,因为这跟地址分配有关。
我们这里的研究只是为了通过这段代码为大家更好地解释引用和指针的区别。

如果还不明白,我们继续看这段代码变量的内存布局图:
这里写图片描述

最后要注意一点的是:引用就是引用,指针就是指针,引用不代表指针,一定不要混淆。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值