- 1、从 *** 作符说起
- 1.1、C/C++中指针 *** 作符 & -> * 的作用
- 1.2、Java中引用 *** 作符 . 的使用
- 2、指针能干,引用干不了的活~
- 2.1、指针可以指向任意一个地址,引用只能指向一个对象
- 2.2、指针可以随意修改所指向地址的数据
- 3、指针的缺陷
- 3.1、野指针
- 3.2、C语言强制类型转换造成的内存误修改
- 4、总结
- 4.1、引用能做到的,指针都能**无损**的做到——反之则不行
- 4.2、指针的灵活带来缺陷,引用的不灵活带来安全
- 4.3、题外话:C++引用和Java引用的区别
点进这篇文章的朋友们,如果对「指针」没有概念,那么请面壁思过。
你不是一个正统的程序员,你是野路子,是faker,在技术这条路上注定走不远。
闲话少述,正文开始。
要看引用和指针的区别,首先要看 *** 作符。
- 在c/c++中,指针相关的 *** 作符有3个:**& -> ***
- 在Java中,引用相关的 *** 作符有1个:.
What,引用就一个 *** 作符???那我们就来看下, *** 作符各有什么作用
1.1、C/C++中指针 *** 作符 & -> * 的作用注:指针使用结构体来举例,便于和引用的对象来比较
-
定义一个结构体和变量
typedef stuct { int sex; int age; } student_t; student_t stu1 = {1, 20};
-
*** 作符 &,将数据的地址读取到一个指针:
int* p_addr = &stu1; // 创建一个指针p_addr,p_addr存储了stu1的地址
-
*** 作符 ->,读/写一个指针所指向结构体地址的成员数据
int age = p_addr->age; p_addr->age = 44;
-
*** 作符 *,读/写一个指针中地址的数据:
student_t stu2 = *p_addr; // 将p_addr地址的数据读取到stu2 *p_addr = {2, 8}; // 将数据写入p_addr地址
1.2、Java中引用 *** 作符 . 的使用注:c++中也有引用,但不在本文讨论范围内
-
定义一个类和对象
public class Student{ public Integer sex; public Integer age; Student(Integer s, Integer a) { sex = s; age = a; } } Student stu1 = new Student(1, 20); // 创建一个引用stu1,stu1中存储的不是对象的数据,而是是对象的地址,也即引用 // stu1存放在stack,对象存放在heap
-
将数据的地址读取到一个引用
Student p_addr = stu1; // 创建一个引用p_addr,把stu1中存储的对象的地址,赋给p_addr
-
读/写一个引用所指向对象地址的成员数据
Integer age = stu.age; stu.age = 44;
注:Java只有引用,没有指针,而引用弱化了地址和数据的概念,所以程序员们更要深刻理解引用的本质,写出更健壮的代码。
如此看来,C/C++指针的 *** 作符 * 能干的活,Java的引用干不了,也就是指针能直接对地址的数据进行读/写,引用则不能。
那咱就来看看,具体那些活是指针能干,引用干不了。
。
。
-
指针可以给用 *** 作符&给其一个数据的地址,也可以直接给其一个地址,甚至空地址
student_t* p_addr = &stu1; student_t* p_addr = 0x12000; student_t* p_addr = NULL;
-
指针可以对地址进行加减 *** 作,从而修改相邻地址的数据,比如修改一个数组
int data[4] = {1,2,3,4}; int* p_addr = data; *p_addr = 6; p_addr += 1; *p_addr = 7; p_addr += 1; *p_addr = 8; p_addr += 1; *p_addr = 9; // 此时数组内数据为:{6,7,8,9}
-
引用只能指向一个对象,不能直接给其一个地址,也不能空引用
Student stu = new Student(); Student stu = 0x12000; // 对不起,编译不通过。
。
。
-
有什么用?
-
在底层驱动开发时,寄存器的地址是固定的,
-
想要修改寄存器的数据,需创建一个指针,把寄存器地址赋给指针,然后去修改寄存器。
// 点亮一个LED int* p_led_addr = 0x1233; // LED寄存器地址是0x1233,将其赋给指针p_led_addr *p_led_addr = 1; // LED亮 *p_led_addr = 0; // LED灭 int state = *p_led_addr; // 读取LED的亮灭状态
-
修改连续地址的多个寄存器
// 点亮多个LED int* p_led_addr = 0x1233; // LED寄存器地址是0x1233,将其赋给指针p_led_addr *p_led_addr = 1; // LED亮 *(p_led_addr+1) = 1; // LED2亮 *(p_led_addr+2) = 1; // LED3亮
-
-
显而易见,引用能不能干???干不了!
-
-
指针大法
Student stu1 = new Student(1, 20); Student* p_addr = &stu1; // 创建Student类型的指针,指向一个stu1 *p_addr = 24242; // 将24242写入stu1的地址
-
引用只能修改所指向对象的固定成员,或者通过所指向对象提供的固定方法来修改数据
-
有什么用?
- 好像没啥用。
。
。
- 好像没啥用。
- 定义
- 指针在创建时,未初始化,此时指向的地址是随机的!此时指针读写,破坏程序运行!
- 指针所指向地址的数据已经被释放,此时指针读写,则破坏程序运行!
- 原因
- 指针可以指向任意一个地址;而引用必须指向一个确定的对象
- 指针不能自动解除指向;而引用在指向的对象销毁时,会自动解引用
- 后果
- 程序奔溃、不能按预期运行、代码漏洞
- 定义
- 将类型A的变量s,强制转换成类型B,然后将其s的地址赋给指向类型B的指针p,对指针p读写
- 此时类型B的数据结构可能并不兼容类型A,导致对变量s的误修改
- 原因
- C语言强制类型转换的不严格检查,过于粗鲁
- 这是C++为什么要引入四个转换符的原因
- 后果
- 程序奔溃、不能按预期运行、代码漏洞
-
指针的 *** 作符 * 能干的活,引用干不了,也就是指针能直接对地址的数据进行读写,引用则不能
-
指针可以指向任意一个地址(甚至空地址),引用只能指向一个对象(不可空引用)
- 指针可以对地址进行加减 *** 作,从而修改相邻地址的数据,比如修改一个数组
- 指针不能自动解除指向;而引用在指向的对象销毁时,会自动解引用
-
指针可以随意修改所指向地址的数据
- 引用只能修改所指向对象的固定成员,或者通过所指向对象提供的固定方法来修改数据
引用避免了对地址的直接读写,增强了内存 *** 作的规范,从而增强了语言内存安全性,降低了对开发者的要求。
指针和引用,各有各的用途,我们理解本质后,在不同的场景选择合适的工具即可!
4.3、题外话:C++引用和Java引用的区别C++中一个引用指向的地址不会改变,改变的是指向地址的内容,然而Java中引用指向的地址在变!!
如果非要对比着看,那么Java中的“引用”倒是和C/C++的指针更像一些,和C++的“引用”很不一样。
java去除指针概念,就用引用罗…
你看 java:
A a = new A(1);
A b = new A(2);
b = a;
没有问题,a 和 b引用同一个对象A(2),原来的A(1)成为没有被引用的对象。
垃圾回收机制会在之后的某个时刻把A(1)干掉。
而C++则不然。
C++的引用就语义上说是“别名”【本质是个const指针,又叫指针常量】,而并不是指针的另一种用法:
A a = A(1);
A b = A(2);
A& c = b; //c 是 b的别名
c = a; //并不是 c 引用 a,而是拷贝 *** 作 c.operator= ( a )
就语言机制来说,java的引用是用来管理和命名对象;
而,C++的引用机制是很纯粹的,就是别名而已,一旦定义就无法修改,即无法再指向其他变量。
每种语言的特性都是整体的有机部分。
我们知道, java的引用机制是一个很复杂的机制。
他必须区分“基本对象”和“复合对象”,你可以想象一下,如果其中没有基本对象,那么我们如何完成对象的复制? 唯一的解决方案是提供两个等于号,或者一律用构造函数… 但是综合来看,他和垃圾回收形成了相当完美的组合方案。
而C++ 的引用机制为运算符重载提供了大幅度的支持。
C++ 的引用是用类“模拟”基本对象的根本要求。
如果C++使用java那种引用,那么原本漂亮的 operator[]、 proxy class 等就很难实现了。
更进一步, C++ 的运算符重载对 C++ 的模版机制提供了强力的支持
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)