目录
一、关于指针
二、指针类型
1、整型指针的访问权限说明:
2、字符指针的访问权限说明:
3、指针的类型决定向前或向后一步走了多大距离
三、野指针相关知识
1、野指针的成因
①指针未初始化
②指针的越界访问
③指针所指向的空间释放了
2、如何规避野指针
①指针要初始化
②要注意指针越界的问题
③指针所指向的空间及时置NULL
④避免返回局部变量的地址
⑤指针使用之前检查有效性
四、常量指针和指针常量
1、常量指针
2、指针常量
五、有关指针的运算
1、指针+-整数
2、指针-指针
3、指针的关系运算
六、字符指针
常量字符串(相关知识)
七、指针数组
数组名的含义
八、数组指针
九、数组参数,指针参数
1、一维数组传参
2、二维数组传参
3、一级指针传参
4、二级指针传参
十、函数指针
函数指针重命名
十一、函数指针数组
一、关于指针
首先内存会划分为小的内存单元,每个内存单元都有一个编号,这个编号就被称为地址,我们把地址也叫指针,如下图所示:
注意注意:指针指的是地址,但是我们口语中的指针通常指的是指针变量
指针变量就是用来存放地址的变量
我们是可以通过修改指针变量的值来修改原数值,如下:
原本a的值是5,通过指针变量pa,解引用也可以改变a的值
关于指针的大小:在32位平台是4个字节大小,在64位平台是8个字节大小,并不会因为指针类型的改变而改变!下方是VS2019的32位平台环境下的演示:
观察可知,无论是整型指针还是字符指针在32位平台下内存大小都是4个字节
注意:32位平台即是32个bit组成的,所以32个bit=4个byte,因此在32位平台指针是4个字节的大小,64位也是相同的道理
二、指针类型
指针类型决定了,指针在被解引用的时候所访问的权限,例如:
整型指针解引用会访问4个字节
字符指针解引用会访问1个字节
下面来具体证明这两个类型的指针的访问权限:
1、整型指针的访问权限说明:
a为16进制0x11223344,在监视页面观察a与*pa相同,都是数值;&a与pa相同,都是地址
观察此时内存中a的存储
输入a的地址,发现内存中存储为44 33 22 11,这里涉及到了我们前面所讲到的大小端存储,不知道的小伙伴可以到“C语言之数据的存储”那篇博客中学习
我们VS中用的是小端存储,所以说为44 33 22 11,接着运行到*pa=0后,我们再观察内存可知:
a的值被更改为了00 00 00 00,4个字节的值都被改了
2、字符指针的访问权限说明:
同样按照刚才的顺序,如下图:
代码部分其他都不变,只变了指针的类型
监视和内存部分一样
当代码运行*pa=0后
我们发现,字符指针类型,解引用只能改变2个16进制位,即8个bit位,即1个byte
上面两个例子证明了不同的指针访问权限是不同的
3、指针的类型决定向前或向后一步走了多大距离
具体意思就是说指针变量加或减一,它的地址变化的量,如下所示:
分别定义了整型指针变量pa和字符指针变量pb, 分别用%p打印地址,观察运行结果可知:
pa和pb地址相同,因为pa和pb都是指向a的地址
而pa+1和pb+1则大大不同,pa+1相对于pa加了4,而pb+1相对于pb只加了1,从这个例子中可以看出指针的类型也决定了,指针向前或向后走一步能够走多大的距离。
三、野指针相关知识
野指针就是指针指向位置是不可知的!
1、野指针的成因
①指针未初始化
这种问题非常常见,具体看如下代码;
运行后报错也会显示这种错误
代码int* p没有初始化,所以p变量中存的地址并没有指向我们当前程序的空间,而是指向内存中随机的空间,因此要*p要访问这块内存空间肯定是出错的!
这里的p就是野指针
②指针的越界访问
这个问题可以说是非常常见的了,我们平常在遍历数组中,一不注意就会遇到这样的问题:
我们设定数组arr,让指针变量p等于数组首元素的地址,数组本身有5个元素,但是在for循环中却循环6次,i分别是0 1 2 3 4 5,本身i是0~4的,循环时多了个5,导致非法访问内存,因此第六个数打印出来是随机数,这里也是野指针的问题了
③指针所指向的空间释放了
这个问题也是经常遇到的问题啦,具体看下方例子:
test函数的返回值是x的地址,main函数中用指针变量p接收x的地址,但是x变量进入test函数创建,而出了test函数会销毁,这时再改变*p的值,即使用x的地址,则是非法访问内存了,也会造成野指针的问题
2、如何规避野指针
①指针要初始化
比如上方的例子中,不能直接int* p,必须要初始化,int a = 10;int* p = &a;从而规避野指针问题
②要注意指针越界的问题
在我们使用数组时,一定要注意数组的元素个数以及我们所循环的次数,避免粗心而导致越界访问
③指针所指向的空间及时置NULL
在我们不使用指针变量p时,int* p = NULL;置为空,在接下来想要使用p时,用if语句:
if(p != NULL) ......
能够很好地避免野指针
④避免返回局部变量的地址
就像上方野指针成因的第三条所举的例子,避免返回局部变量的地址
⑤指针使用之前检查有效性
像if(p != NULL) ......就是在检查指针的有效性
四、常量指针和指针常量
常量指针和指针常量在题目中遇到的可算是非常多啦,它们都与const这个关键字有关,下面举三个例子,看看大家能不能分清其中的区别:
const int *pa = &a;
int const *pa = &a;
int *const pa = &a;
1、常量指针
常量指针的概念是:指针所指空间的值不能发生改变,不能通过指针解引用修改指针所指向空间的值,但是指针的指向是可以改的
2、指针常量
指针常量的概念是:指针本身就是一个常量,即指针的指向不能发生改变,但是指针所指向空间的值是可以发生改变的,可以通过解引用改变指针所指向空间的值
具体怎么更好的理解呢?
大家先看常量指针这个名字,以指针结尾,所以是具有指针的性质的,因此可以改变指向,又因为是常量指针,所以指向的值是常量的,即是不能改变的
再看指针常量这个名字,以常量结尾,应该具有常量的一些性质,所以自然不能改变指针的指向,只能修改指针所指向的值啦
那么const究竟加到代码的哪个位置才叫常量指针,加到哪个位置才叫指针常量呢?
说一个小窍门可以很快的帮助大家记住:
const如果在*的左边,那就是常量指针
const如果在*的右边,那就是指针常量
至于为什么要这样记呢,可以思考一下,*在指针中有解引用的作用,那么const在*左边,可以理解为const修饰*,即不能修改指针所指向空间的值,但是指针的指向确是没有限制的
那么const在*右边,修饰的就是指针变量啦,即指针变量不能改变它的指向,但是指针所指向空间的值不受限制
那么上面的三个例子就很明显有答案了,只需要看const修饰的是*还是指针变量即可
常量指针:const int *pa = &a;、int const *pa = &a;
指针常量:int *const pa = &a;
五、有关指针的运算
1、指针+-整数
举个例子简单说明指针加减整数的情况:
指针变量p是数组第一个元素的地址,用for循环打印数组各个元素,*的优先级高于++,所以每次执行*p打印完数组中元素后,p++再指向数组下一个元素,从而循环5次打印出数组中5个元素
2、指针-指针
指针-指针的操作得到的是指针和指针之间元素的个数,当然前提是两个指针必须指向同一块空间,下面看例子;
具体画图进行如下讲解:
如上图所示表示数组中5个元素在内存中的存储,其中p1指向第一个元素的地址,p2表示第五个元素的地址,代码中用p2-p1,就表示的是两个指针之间的元素个数,很清楚的看到是4个元素,所以打印出来结果为4
3、指针的关系运算
指针的关系运算就是指针之间进行大小的比较,从而实现某些功能,例如:
圈出来的即为指针的关系运算,下方所画的图可以清楚解释:
p刚开始是数组首元素的地址,接着通过p