引用和指针的区别
一.引用和指针
- 指针就是内存地址,指针变量是用来存放内存地址的变量。不同类型的指针变量所占用的存储大小都是相同的(32位 -- 4个字节;64位 -- 8个字节)。
- 引用不是新定义一个变量,而是给已存在变量取一个别名,编译器不会为引用变量开辟内存空间,它和引用对象公用一块内存空间。并且引用类型和引用实体必须是同种类型的。
- 引用可以做的,指针都可以做,那么为什么还要用引用呢???
- 引用具有指针的效率,又具有变量使用的方便性和直观性。
- 指针能很自由的操控内存,虽然功能强大,但是也十分危险。
二.引用和指针的区别
引用 | 指针 | |
初始化 | 必须初始化 | 没有要求(尽量初始化,防止野指针) |
重新赋值 | 引用一旦初始化后,不能重新赋值指向其他变量 | 指针可以在运行时重新赋值指向其他变量(除非是指针常量) |
非空 | 没有NULL引用 | 有nullptr指针 |
sizeof大小 | 为引用对象的实际大小 | 32位 4个字节;64位 8个字节 |
++ | 为引用的实体增加1 |
意思是指针向后偏移一个类型的大小 |
多级 | 没有多级引用 | 但是有多级指针 |
安全性 | 相对安全 | 安全性相对低一点 |
三.引用和底层原理
在语言层面,引用的用法和对象一样.在二进制层面,引用一般都是通过指针来实现的,只不过编译器帮我们完成了转换.
可以通过定义引用和指针变量进行调试观察:
int main() { int a = 10; int *p1 = &a; int& p2 = a; return 0; }
通过编译器调试观察反汇编语言发现,引用和指针的反汇编语言是一样的(编译器帮我们将我们完成了转换).
底层实现:引用通过指针实现 (定义一个引用类型的变量相当于定义于一个指针类型的变量)
语法: 引用是别名,不是指针,没有发生拷贝(目前可以认为引用是指针的一层封装,更加简单化的指针)
注意:在语法概念上,引用就是一个别名,没有独立空间,和其引用实体共用同一块空间.但是在底层实现上实际是有空间的,因为引用是按照指针方式来实现的.
四.const修饰的引用和指针
之前我们就知道,对于指针而言const的位置可以决定其修饰的对象是谁,例如常量指针和指针常量;那么引用呢?
常量引用和常量指针
常量指针:const int* p; 实际为指向常量的指针,指向的值不能改变,而指向可以改变。
常量引用:const int& p; 与常量指针一样,不能对指向的对象的值进行改变;注意,因为引用的定义,指向也不可以改变。
引用常量和指针常量
指针常量:int* const p; 实际上是一个指针类型的常量,指向不能变,指向的值可以改变。
引用常量:上面讲了,引用本身的定义就是一旦初始化引用的对象后,就不能重新对该引用变量进行赋值了,但是可以修改引用对象的值。是不是就和引用的定义一样!
所以语法上并不需要加上const修饰。
常量指针常量和常量引用常量
因为引用不能重新赋值,即指向不能变,所以实际上只有常量引用,而没有引用常量。
五.指针传递和引用传递
- 指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。
- 引用传递过程中,被调函数的形式参数也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量(如果一定通过指针的方式改变主函数的相关变量,那就得使用指针的指针,或者指针引用,我们一般都使用指针的指针)。
不论是指针传递还是引用传递,其效率都远远超过值传递,尤其在处理一些比较大的对象,值传递需要更多的时间开销并且占据更多的内存,因此在传值过程中,尽量使用指针传递和引用传递,而因为相比于指针引用的直观性(指针存在多级指针不方便去理解)和可读性,我们建议能使用引用就使用引用传递,尽量不使用值传递,必须使用指针传递才使用指针。
原文链接:https://blog.csdn.net/qq_61313949/article/details/124802569
原文链接:https://blog.csdn.net/HUAERBUSHI521/article/details/118368696