"迷途"的野指针,都快找不着北了

指针,C语言开发者表示很淦,指针的使用,很多人表示不敢直面ta,不像Java一样,有垃圾自动回收功能,我们不用担心那么多内存泄漏等问题,那C语言里边呢,指针又分为了"野指针","迷途指针" 。 你是不是更迷糊了,这篇一起来攻克ta!

  • 发现我的封面似乎致敬了一下路痴"索隆",刚好跟我们今天的主角一样,找不着北的"迷途"指针hhh

悬垂指针/迷途指针定义

  • 当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称悬垂指针(也叫迷途指针)。

野指针定义

  • 野指针指的是还没有初始化的指针(导致我们不知道他会指向哪里)
	int* p;
	//没有初始化就使用他
	printf("%p", p);

编译器一般会提示你出错了!
image.png

常见错误:

1。指针定义时未被初始化:

  • 指针在被定义的时候,如果程序不对其进行初始化的话,它会随机指向一个区域

这种指针就是我们上边所说的野指针

//先定义一个指针(没有初始化)
int *p ;
//然后就使用这个指针
*p =12;

2。指针释放后没有置空:

  • 我们在用malloc()开辟空间的时候,要检查返回值是否为空
    • 如果为空,则开辟失败;
    • 如果不为空,则指针指向的是开辟的内存空间的首地址。
  • 指针指向的内存空间在用free()或delete释放后,此时指针指向的就是“垃圾”内存。如果我们没有对其进行置空或者其他赋值操作的话,就有可能会成为悬垂指针/迷途指针 。

free

free() 只能释放动态分配的内存空间,并不能释放任意的内存。下面的写法是错误的

int a[10];
free(a);

注意:free() 不会改变 np 变量(一个指向结构体的指针)本身的值,调用 free() 后它仍然会指向相同的内存空间
image.png

可以把他理解为,原本np指向了一个房子,现在free了,把房子里的人赶了出去,而np仍然指向这个房子

但是此时该内存已无效,不能被使用。
把他们打印出来的话还是一样的,但是再访问的话
image.png
free之后访问到的数据就奇奇怪怪的了

规避方法

建议在 free操作后 将 np 的值设置为 NULL,例如:

free(np);
np=NULL;

3。在方法中定义变量,返回该变量的地址

  • 先来看一个实例
#include"stdio.h"
int* tempAddress() {
	int temp1[] = { 1,2,3 };
	return temp1;
}
int* tempAddress2() {
	int temp2[] = { 4,5,6 };
	return temp2;
}
int main() {
	//第一次
	int* temp = tempAddress();
	printf("%d\n", *temp);
    //调用另一个完全不一样的函数
	tempAddress2();
    //重新输出
	printf("%d\n", *temp);
}

image.png
诶?明明第一次还能输出出来来着,怎么后边调用了另一个完全不一样的函数之后就输出变得那么奇怪了呢?

  • 要知道,我们每个方法执行的时候,都会为这个方法去分配一个栈帧,当这个函数返回时,为栈帧分配的那一块内存区域可能就已经被释放了,但是数据还未清理
  • 但是任何接下来的函数调用都可能又会把刚才释放掉的那块空间给占用了。导致再输出temp所指向的值时,就变成我们新修改的值了

可能看到这里有的同学会有疑问了?诶,那free跟这个方法栈的回收有什么区别呢?为什么free过后是直接不能访问呢,而方法栈的回收却只是把区域回收了,但是数据还没有清理掉

其实我们free,通常都是搭配malloc来使用的,何谓malloc,malloc是我们程序员手动在中开辟一个空间,可以自己进行管理,不会像栈一样,出了函数就自动被回收了

来看我们用malloc能不能解决上边那个问题

int* tempAddress() {
	//int a[] = { 1,2,3 };
	int* a = (int*)malloc(sizeof(int) * 3);
	a[0] = 1;
	return a;
}
int* tempAddress2() {
	//int b[] = { 4,5,6 };
	int* b = (int*)malloc(sizeof(int) * 3);
	b[0] = 4;
	return b;
}
int main() {
	int* temp = tempAddress();
	printf("%d\n", *temp);
	tempAddress2();
	printf("%d\n", *temp);

image.png

  • 是不是美滋滋,我们可以自己手动控制这块区域了,要回收也是我们自己回收,不会说出了函数体就被自动回收了

堆栈需要注意的问题

内存空间不足,内存泄漏

malloc一时爽,一直malloc一直爽,随着我们开发不断的malloc,可能会带来什么问题呢?

  • 最显而易见的当然是我们的内存空间分配的问题,当我们malloc的数量,空间内存越来越大时,势必会造成堆内存的无限膨胀,。长期运行将会导致可用内存越来越少,程序也将会变得越来越卡顿。如果我们不手动释放,那就要到程序结束才会释放。可能会出现内存泄露(就是我们前边所提及到的)等问题

堆栈溢出/缓冲区溢出

使用过多的存储器时导致调用堆栈产生的溢出,也是缓冲区溢出中的一种

  • 一般在递归中产生。堆栈溢出很可能由无限递归(Infinite recursion)产生,但也可能仅仅是过多的堆栈层级。

最常见的情况就是无限递归了

int fun()
{
    //这里递归没有一个终止条件,会无限调用
    return fun();  
}

写在最后

  • 关于递归,在之后的数据结构专栏我们还会涉及到(归并排序和快速排序等) , 还有不同语言的垃圾回收机制, 等到melo进一步学习了Java中的垃圾回收后,我们再来聊一聊!
posted @ 2021-10-21 09:29  Melo~  阅读(115)  评论(0编辑  收藏  举报