C语言之指针篇【超详细讲解,带你层层深入理解指针】

慈云数据 2024-05-29 技术支持 20 0

目录

一、关于指针

二、指针类型

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

微信扫一扫加客服

微信扫一扫加客服

点击启动AI问答
Draggable Icon