一. 野指针
1. 野指针的成因:
野指针的成因有三种,我们对它们一一进行介绍:
(1) 指针未初始化:
倘若我们要创建一个指针变量p来存放a的地址,当出现这样的情况,指针未初始化:
int *p;
这个指针变量未初始化,默认为随机值。
(2) 指针越界访问,例如:
我们可以看到,当指针指向的范围超过数组的范围(11)时,p就是野指针。
(3) 指针指向的空间释放:
我们通过一个例子来说明:
这个代码存在严重的逻辑错误,我们对它来进行分析:
首先,我们创建了一个指针变量p,并用它来接收test传来的函数地址。
其次,我们跳到test中,创建了一个变量n,并将其赋值为100,再返回这个值所在的地址。
最后,我们本来想做的是,通过解引用p找到test函数中存放的地址的内容,并将其打印。
🙅♂️但是,n是一个局部变量,在出test函数之后,创建的n的地址在内存中就会被销毁,这时候虽然指针变量p中还存放着n的地址,但是再解引用p来找内存中的地址,就会找不到了。
2.避免野指针
对于野指针的成因,我们分别给出了相应的解决办法:
(1) 注意指针初始化:
倘若我们明确一个指针指向哪里,就直接赋值地址,当不知道指针指向哪里时,可以给指针赋值为NULL,具体的格式是这样的:
Int *p=NULL;
(2) 仔细检查以避免指针越界。
(3) 避免返回局部变量的地址。
二. assert断言
在使用这个函数的时候,我们需要加入头文件assert.h
以下是它的使用方法:
assert(p!=NULL)
当代码运行到这里时,如果变量p是NULL,程序就会报错,就像这样:
三. 指针的传址调用
假设我们需要写一个函数,来完成两个数的交换,正常来写是这样的:
void change(int a, int b)
{
int c = 0;
c = a;
a = b;
b = c;
}
int main()
{
int a = 10;
int b = 20;
change(a, b);
printf("a=%d b=%d", a, b);
return 0;
}
从打印结果来看,两个数并没有进行交换,原因如下:
我们的函数change(a,b)里的a,b是实参,而void change(int a, int b)里的a,b为形参,当实参给形参传递内容的时候,形参会临时单独创建一份空间来接收实参,这也就意味着,在形参中的a,b两个变量发生了交换,但是在出函数的瞬间,a,b变量所在的临时空间与其地址会被销毁,这时候实参中的a,b并不会发生变化。
但是,当我们用指针来写的时候,就可以彻底完成两个变量的交换:
void change(int* a, int* b)
{
int c = *a;
*a = *b;
*b = c;
}
int main()
{
int a = 10;
int b = 20;
change(&a, &b);
printf("a=%d b=%d", a, b);
return 0;
}
我们通过change函数,把a和b的地址传给void change(int* a, int* b),而在void change(int* a, int* b)中,我们用a和b两个指针变量来接收其传来的地址,而这个时候,给指针变量a和指针变量b解引用,它们所找到的地址的内容就是实参a,b的地址所对应的内容。我们通过函数远程操控地址,从而实现了两个变量的交换。这种将变量的地址传递给函数的方法,叫做传址调用。