动态内存管理详解

C语言中开辟内存有很多种方式,目前我们最常用的也就是数组,但数组是在我们用到他之前就得设定好它的长度,有时很不方便。
我们知道,c语言规定,不允许设定一个未知长度的数组但在Linux下可以设定,但也不支持这样做
下面这段代码就会报错哟!!!

	int x = 5;
	int arr[x];

 所以,为了填补这一缺口,c语言有了动态内存,c语言提供了几个函数来管理我们的动态内存,这几个函数非常重要,分别为:

一:
1:malloc

void* malloc(size_t size); 

 可以看到这个函数的返回类型为void*,为一个空指针(我们前面了解到,void*可以作为返回值和传参,但不能直接解引用,所以我们在运用它时需要先将它强制转化为我们想要的指针类型);
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针;
如果开辟成功,则返回一个指向这块空间的地址;
如果开辟失败,则返回NULL,因此对malloc函数的返回值一定要先检查再使用;

我们知道,以前我们定义的局部变量、函数、数组所需要的空间都是在栈上开辟的,但切记malloc申请的内存是在堆上开辟的栈上的可用资源远远小于堆上的,所以malloc的专长是用来提供大内存需求,而且方便管理
(这里提到的栈和堆是什么,我们可将计算机的内存结构画成如下以便我们理解)

再提供一张栈空间的结构示意图:

 可以看到,堆空间是向上生长的,当我们用malloc函数开辟空间时,就会在堆上开辟一段连续的内存空间。


2.free

void free(void* ptr);

有了开辟内存,则就必须要有释放内存!!!!!!!!!!!!
c语言提供了这样一个函数来释放我们所开辟的内存;

注意:如果ptr不是指向动态内存的,则这种行为未定义;如果ptr为NULL,则函数什么都不会做;

下面来看一个简单的动态内存开辟例子:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* ptr = NULL;
	int num = 0;
	scanf("%d", &num);
	ptr = (int*)malloc(num * sizeof(int));
	if (NULL != ptr)   //必须判断
	{
		int i = 0;
		for (i = 0; i < num; i++)
		{
			*(ptr + i) = i;
		}
	}
	else
	{
		perror("malloc");
	}
	int* p = ptr;
	for (; p < ptr + num; p++)
	{
		printf("%d\n", *p);
	}

	free(ptr);      //勿忘
	ptr = NULL;    //有必要这样做,不然ptr成了野指针;
	return 0;
}

perror函数是一个自动生成错误信息的函数;可以自己去c库里查看。
 


3.calloc

void* calloc(size_t num, size_t size);

该函数的功能为为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为
该函数与malloc函数基本上没有啥区别,唯一区别就是可以把开辟的内存自动初始化;初始化每个字节为0

所以如果要求申请内存需要初始化,这个函数就很方便。


4.realloc

void* realloc(void* ptr, size_t size);

当我们申请的内存需要扩展或者缩减时,怎么办呢?
这时,c语言提供了realloc这个函数,让动态内存管理变得更加灵活;

参数介绍:
ptr是要调整的内存地址,size为调整之后的新大小;
返回值为调整之后的内存起始位置;
这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间;

realloc在调整内存时存在两种情况:
1.当原有空间的后面有足够大的空间的时候  : 要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化;
2.当原有空间的后面没有足够空间时 : 在堆空间上另找一个合适大小的连续空间来 使用。这样函数返回的是一个新的内存地址;

来看一个例子:

#include<stdio.h>
int main()
{
	int* ptr = malloc(100);
	if (ptr != NULL)
	{
		;//执行任务
	}
	else
	{
		perror("malloc");
		//或者 exit(EXIT_FAILURE);
	}
	//第一种方法扩展
	//ptr = realloc(ptr, 1000);     //第一种方法;(可能会造成内存泄漏)

	//第二种方法扩展
	int *p = realloc(ptr, 1024);
	if (p != NULL)
	{
		ptr = p;
	}
	else
	{
		perror("realloc");
	}
	//执行任务

	free(ptr);
	return 0;
}

我们在使用realloc时,一定要判断他返回的值,不然如果开辟失败,则会返回NULL,第一种方法的话,ptr就指向了NULL,原来的指针就找不到了,free不掉了,就造成了内存泄漏;


二:我们知道,只要开辟内存就会有限制,我们来写一个代码查看自己电脑大概最多能开辟多少栈上内存;

int main()
{
	int* ptr = NULL;
	int num = 0;
	scanf("%d", &num);
	ptr = (int*)malloc(num*1024*1024*sizeof(char));
	if (NULL != ptr)   //必须判断
	{
		;
	}
	else
	{
		perror("malloc");
	}
	free(ptr);      //勿忘
	ptr = NULL;    //有必要这样做,不然ptr成了野指针;
	return 0;
}

代码中的1024*1024*sizeof(char)代表一兆大小的内存;
可以自己去测试,当想要获取的内存不足以提供时,则会执行else语句,这里的perror函数是stdio.h中的一个库函数,它的作用是打印一个错误信息;

这些都是动态内存管理的基础必备知识,下次会引入一些深入概念以及一些经典题型!!

猜你喜欢

转载自blog.csdn.net/eternal_yangyun/article/details/84583435