神奇的指针

目录

前言

一、什么是指针

二、指针和指针类型

三、野指针

四、指针运算

五、指针与数组

六、二级指针

七、字符指针

八、指针数组

九、数组指针

十、数组与指针传参

十一、函数指针

十二、指针练习题


前言

const在*左边时,*p不能被修改,但p可以被修改

 const在*右边时,*p能被修改,但p不可以

 

一、什么是指针

内存是由多个小格子组成,而每一个格子的大小为1byte,这些小格子也被称为内存单元

每个内存单元都有一个编号(地址),通过编号就能找到对应的内存单元,所以指针是变量,用来存储内存单元的地址(编号)

如上图:p就是个指针变量,存放了a的地址

总结:指针是用来存放地址的变量

注意:地址在内存中是用十六进制表示的

二、指针和指针类型

由于数据有int、char、float等等类型

则相应的指针也有int*、char*、float*,用来存放对应类型的数据的地址

注意:指针的大小都是4byte(32位机器)或者8byte(64位机器)

指针+-整数

指针的类型决定了它+-整数,跳过几个字节

指针的解引用

指针的类型决定了它解引用时,能操作几个字节

 

 三、野指针

定义:指针指向的位置是不可知的

造成野指针的三个原因

指针未初始化

 

指针越界访问

当指针指向的范围超出数组的范围时,p就是野指针

 指针指向的空间释放

 

上图,pa是个局部变量,当Add函数结束时,pa也会被销毁,所以p是个野指针,至于第一张图还能正常打印,是因为其值没有被覆盖掉

如何规避野指针

指针初始化(如果指针没有明确的指向对象时,可以初始化为NULL)

小心指针越界

指针指向的空间被释放时,应使指针置为NULL

避免返回局部变量的地址

指针使用之前检查其有效性

四、指针运算

指针+-整数

指针-指针

前提:两个指针指向的是同一块区域

例如:当指向数组时,得到的是所指向的两个元素之间的元素个数

 

 指针的关系运算

  例如:int a[5] ={1,2,3,4,5};

          int* p1 = &a[0];

          int* p2 = &a[4];

          p1<p2,这种比较运算就是指针的关系运算

 五、指针与数组

指针指向数组,从而可以利用指针来访问数组成员

a[i] 等价于 *(a+i)如下图

此时数组名a是首元素的地址,等价于&a[0]

指针变量(指针)p存储了a的地址,也可以说p指向了a

 六、二级指针

由于指针(指针变量)也是个变量,是变量就有地址,那就能被存储,存储它的则是二级指针

同样,二级指针也是个变量,由此可定义三级指针等等

 

 如上图,ppa存储的是pa的地址,所以*ppa = pa,而pa存储的又是a的地址,所以*pa = 2

 即**ppa = 2,能得到**ppa = 2等价于*pa = 2等价于 a = 2

七、字符指针

第一种,指向一个字符,如下图

第二种,指向一个字符串,但本质上是保存了字符串的首元素的地址,如下图

 

 注意:上图的"hello world",是常量字符串,不能被修改

八、指针数组

概念:是数组,只是存放的每一个元素的都是指针(地址)

九、数组指针

概念:是指针,存放的是数组的地址

例如:int a[3]={1,2,3};

int (*p) = &a; p指向这个数组名为a的数组,这个数组有3个元素,每个元素是int类型

用处:例如二维数组传参,可以用数组指针做形参接收,如下图

十、数组与指针传参

一维数组传参

注意:上图中实参中的arr是首元素地址,所以上述三种形式本质上都是第三种,是指针,所以第二种数组中的索引无论是100还是1000都没有任何关系

二维数组传参

注意:第三种是错误的,因为二维数组传参只能省略行,不能省略列

一级指针传参

三种都是一级指针,所以传参也都是用一级指针接收

二级指针传参

因为arr[3]是指针数组,所以它的每一个元素都是int*,所以arr是首元素int* xx的地址,也就是二级指针,传参时用二级指针接收

因为pa是一级指针,那么pa的地址就是二级指针,传参时用二级指针接收

十一、函数指针

概念:指向函数的指针,存储的是函数的地址

例如:函数指针:int (*pf)(int,int) = &Add,两个int都是参数,去掉pf就是函数指针类型

函数名与&函数名是完全一样的,都是表示函数的地址,所以下图两种写法都可以

 例题:

(*(void (*)())0)();这是函数调用

解释:void (*)()是一个无参数的函数指针类型,而( void(*)() )0,则是把0强制类型转换为函数指针类型的一个函数的地址,前面的*则是对0这个地址进行解引用,去0地址处的这个函数,被调用的函数是无参,返回类型是void

void (*signal(int , void(*)(int)))(int);这是一个函数声明,声明的函数名是signal

signal函数,它有两个参数,一个类型是int,另一个是函数指针类型void(*)(int),它的返回类型是void (*)(int)的函数指针类型

函数指针数组

用途:转移表,想调用对应的函数时,就直接传对应的函数的地址 

指向函数指针数组的指针

总结:名字先与哪个结合,就是什么类型,与*先结合,则是指针,与[]结合,则是数组

十二、指针练习题

注意:只要是地址(指针),就是4/8个字节,二维数组可看成是一个一维数组,它的每一个元素都是一个一维数组,可利用这点来处理上述的第三个例题

注意:打印十六进制时,左边只有0可省略 

 注意:打印%p,和打印无符号整数是一样的,只是是16进制

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值