通过上文的学习,相信大家一定对指针有个一个初步的了解,那么接下来呢,我们就要对指针进行进一步的了解!
一.野指针
概念:野指针,就是指针指向的位置是不明确的,不可知的,随机的。
1.野指针成成因
那野指针是如何形成的呢?它有以下几个成因:
(1).指针未初始化
(指针变量未初始化,则指针变量的值为随机值)
例如:
(2).指针越界访问
这种情况主要出现在数组中,例如:
(3).指针指向的空间释放,例如:
但是野指针这种情况是可以避免的,那么如何规避野指针呢?
2.如何规避野指针
我们可以根据野指针的成因一一规避,对症下药!
(1).初始化指针变量<-指针未初始化
(2).防止指针越界访问<-指针越界访问
(3).避免返回局部变量的地址
局部变量的生命周期随着所在函数的栈帧的创建而创建,随着其销毁而销毁,所以当函数栈帧销毁时,指针指向的空间被释放,这时候我们再去返回局部变量的地址,是会出现野指针的。具体可见上文说到的野指针成因3。
(4).当指针变量不再使用时,及时置NULL,,指针使用之前检查有效性。
如果把中野指针比喻成野狗,那么及时置NULL,可以将这条野狗“栓”起来,防止因为野指针造成程序里不必要的错误生成。
置NULL之后,我们约定,此NULL指针不再被访问。
需要注意的是,即使我们用NULL“栓”起野指针,我们也不能直接使用此指针。
二.assert断言
宏assert(),它用于运行时确保程序符合指定条件,如果()中的符合指定条件,那么程序继续进行,如果不符合,则会停止运行,这个宏被称为“断言”。
在使用此断言的时候,我们需要声明其头文件:
#include<assert.h>
assert断言对我们敲代码是非常友好的,他有很多优点:它能自动识别程序出问题的行号,并且当我们已经确定程序没有问题无需断言的时候,我们可以启用一种无需更改代码就可以关闭或打开assert断言的机制:在#include<assert.h>前定义一个宏NDEBUG:
#define NDEBUG
而当程序再次出现问题,我们移除这条语句,assert断言再次被启用。
但是assert断言的使用增加了检查代码的时间,从而会增加程序运行时间。因此,在debug版本中我们使用此断言来帮助我们检查错误,而在release版本中直接优化掉了assert,从而提高程序运行效率。
三.指针的传值调用和传址调用
1.用指针传址调用模拟strlen函数的实现
2.传值调用与传址调用
当我们想交换两个数的值时,我们设计了一个函数,如下:
可是运行结果却不尽人意:
因此我们对程序做出调整:
我们不再将a,b的值直接传入,而是将a,b的地址传入,用指针来接收地址,这时我们可以发现,a与b的值成功发生交换。
那这是怎么一回事呢,实际上,实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,形参与实参上的变量实际上占用了不同的内存空间,拥有不同的地址,对形参的修改不影响实参。因此,在第一次的代码中,发生的是传值调用。而在第二次的代码中,发生的是传址调用,让函数和主调函数之间建⽴真正的联系,在函数内部可以修改主调函数中的变量。
所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采⽤传值调⽤。如果函数内部要修改 主调函数中的变量的值,就需要传址调⽤。
好了,关于指针(一)下的讲解就到这里,感谢大家的阅读,欢迎批评指正,我们下期再会!