24 指针

1、指针是什么

指针就是地址,平常说的指针通常指的是指针变量,指针变量是来存放内存地址的变量。

da37939371124bbbaaf94c671119f551.jpg

一字节对应一个地址

在32位平台下,一个指针的大小是4字节

在64位平台下,一个指针的大小是8字节

2、指针和指针类型

指针也是有类型的,但是指针的类型改变的并不是指针的大小(在对应平台下,指针的大小是不变的)

cb0a68295a684dc78982867f2d6e0c4e.jpg

那指针类型的意义是什么呢?

1.指针+-整数

c6d64802024c4828bb0b020731d590ca.jpg

通过计算我们可以看到,pc和pi其实并无差别因为都是首字节的地址,但是+1之后,跳过的字节并不相同

总结:指针的类型决定了指针向前或者向后走一步有多大(距离)

2.指针的解引用

ce81273c823543d7b1c4ffc6b3d74d75.jpg

通过计算可以看到,pi可以将n改为0x00 00 00 00

而pc只能将n改为0x00 22 33 44

也就是说int*指针解引用时可以访问4个字节

char*指针解引用访问1个字节

总结:指针的类型决定了,对指针解引用的时候有多大的访问权限(能操作几个字节)

3、野指针

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

空指针 ≠ 野指针

空指针是NULL

1.野指针成因

a.指针未初始化

01966fe03faf4781a3ffc173161802ce.jpg

b.指针越界访问

279c3e1b601b4debbe2338dda43ffc57.jpg

c.指针指向的空间释放

2.如何规避野指针

a.指针的初始化

b.小心指针越界

c.指针指向空间释放,及时置NULL

d.避免返回局部变量的地址

03b16d0128ae432ea7a3f5866bf071b4.jpg

返回的时候a变量的空间被释放没有权限再访问了 所以&a指向不明确 所有造成了野指针现象

p存放的是a的地址没错,但是a在出作用域时已经销毁了,就算找到那么地址存放的也有可能不是a,因为可能会被其他语句给占用

比如

cc9ef8d9efe94eaab7494cb01e5f5101.jpg

这里打印的*p就不再是10了而是随机值

e.指针使用之前检查有效性

ebb70fc6f2304315a622add5245a69dd.jpg

4、指针运算

1.指针+-整数

5c7eb883d09d41ef9e8d479cdd9d1308.jpg

这里的第二行是创建了一个values数组

*vp++ = 0;可以拆成两步:

因为后置++所以先解引用*vp = 0;

然后再vp++;

*vp指向的是地址所对应的值,vp指向的是地址

画图理解:

be8b5faa48c84c1caa4a47686117cd19.jpg

其实上面类似下面这道题:

如何给数组每个元素赋值

5d617a5833d347c494bb1731a0791e14.jpg

arr是首元素地址直接赋给p

还可以这么写:

f5408a81d5e24e09a0592257e52e0eab.jpg

这里的p+i就相当于第i个元素(i从0开始),很巧妙

2.指针-指针

06c574882be546f9abf764cc4167b718.jpg

指针-指针得到的绝对值,是两个指针之间元素的个数

不是所有的指针都能相减,指向同一块空间的两个指针才能相减

7a1b929854924be0bbb60e92e435aa17.jpg

这里可以用指针-指针来实现自定义的strlen函数

指针+指针没有意义

就相当于日期+日期没有意义一样

3.指针的关系运算

853aaaef9df9498fb07a081673cdfd65.jpg

画图理解:

3d01f76db18143ec8a22e5a00948b78a.jpg

此时只是访问了第五个元素的地址,但是没有修改就不算越界

代码简化:

046d1a1af2724faa836a4636868a29f0.jpg

但是简化之后,会访问第0个元素前面一位的地址

59ae073ad7174057a84d3f3a6988c506.jpg

我们应该避免这样写

5、指针和数组

数组:一组想同类型元素的集合

指针变量:是一个存放地址的变量

数组名和数组首元素的地址是一样的

(两种情况除外,数组章节讲过了)

那么就可以把首元素的地址存放到指针变量中去

int* p = arr;

既然知道了首元素的地址,那么我们就可以通过指针来访问每一个元素的地址

ef343a1ba5224a48bf42685141b68c0a.jpg

我们可以发现,&arr[i]和p+i的地址是一一对应的

那我们就可以通过指针来访问每一个数组元素了

9e3f7cdb824b40d0b5723fdffe716f8f.jpg

即*(p+i),因为p里面存放的是首元素地址,那也可以写成*(arr+i)

那么我们就可以知道其实arr[i]等价于*(arr+i)

6、二级指针

指针变量也是一个变量,是变量就有地址,那么指针变量的地址存放在哪里?

这,就是二级指针。

写法:

int a = 10;

int* pa = &a;

int** ppa = &pa;

pa是一级指针,ppa是二级指针

对二级指针的运算:

  • *ppa通过对ppa中存放的地址进行解引用,这样找到的是pa,*ppa其实访问的是pa

int b = 20;

*ppa = &b;//等价于pa = &b;

  • **ppa先通过*ppa找到pa,然后对pa进行*pa操作即对pa解引用,找到的是a

**ppa = 30;

//等价于*pa = 30;

//等价于a = 30;

不能理解为地址的地址!!!

7、指针数组

指针数组是存放指针的数组

4c5869f5e53d40dcabcf84cbf457da28.jpg

这就是一个简单的指针数组

那我们可以想到,数组名是数组首元素的地址,那么我们可以通过指针数组来实现二维数组的创建。

e3c7abd5b0c74b6ba7f8617a2e3d21b9.jpg

parr[i][j]为啥不用解引用?

[ ]就相当于解引用,因为arr[i]等价于*(arr+i)

其实可以理解为用parr[i]代替了arr

parr是数组,所以parr[0]等价于数组第一个元素,即arr1,所以parr[0][j]可以理解为arr1[j]

第一个参数i是指针数组对应的arr数组下标,第二个参数j是对应arr数组中的元素下标。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值