动态内存管理——C语言

目录

1.为什么会有动态内存管理

2.动态内存空间的开辟

1、malloc

2、free

3、calloc

4、realloc

三、常见的动态内存的错误

4.总结

前言:动态内存是想要学会数据结构必不可少的一个知识,本章详细讲解了动态内存的概念和知识以及相关应用,让大家深刻认识和理解动态内存管理。

1.为什么会有动态内存管理

1.我们一般的开辟空间方式:

int a = 0;//申请4个字节空间
int arr[10] = { 0 };//申请40个字节空间

2.这样开辟空间的特点

(1)申请的空间大小是固定的
(2)像数组那样一开始就要确定大小,一旦确定大小就不能改变了

3.动态内存

对于程序来说上述的内存申请是不能满足 因此为了能够对内存进行调整,C语言引入了动态内存开辟,让程序员自己可以申请和释放空间,就比较灵活了。

2.动态内存空间的开辟

我们之间开辟数组空间和其他结构体空间时,都是在栈上进行开辟的,而动态内存空间的开辟是在堆上进行的。并且都申请内存的函数和释放函数都包含在头文件 :#include<stdlib.h>

1、malloc

(1)返回类型和参数:

void* malloc(size_t size);//返回类型为 void*  ,参数为正整数单位是字节

因为返回类型为 void*,所以在malloc函数是不知道我们想要申请什么类型的空间,面对这种情况我们要将返回的类型进行强制类型转换,这样就能返回我们需要的类型了,参数是申请的大小。

(2)作用:

向内存申请一块连续的空间,并返回指向这块空间的的指针

(3)注意:

a.在申请完之后我们还要判断是否成功申请
当申请失败时会返回NULL
当申请成功后就返回指向这块空间的的指针,这样可以正常使用这块空间了


b.如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器

(4)使用:

#include<stdio.h>
#include<stdlib.h>
int main() {
	int* p = (int*)malloc(sizeof(int) * 10);//向内存申请40个字节空间
	if (p == NULL)//判断是否申请成功
		return 1;//失败直接放回
	for (int i = 0; i < 10; i++)//成功就正常使用
		*(p + i) = i;
	for (int i = 0; i < 10; i++)//打印
		printf("%d ", *(p + i));
	return 0;
}

运行结果:

2、free

(1)返回类型和参数:

void free( void* p)//参数为向动态内存申请的空间的指针,返回类型为空

(2)作用:

专门进行对动态内存的释放和回收

(3)注意:

a.当释放的内存不是动态开辟的,这是free未定义的
b.当p是NULL时,free函数什么事都不发生
c.当我们将p指向的空间释放后,要将p置空,不然p就成野指针了

(4)使用

在我们上一个代码中,并未对malloc函数开辟的空间进行释放,其实这是不对的,这会造成内存泄漏。 我们可以通过free进行空间的释放。

#include<stdio.h>
#include<stdlib.h>
int main() {
	int* p = (int*)malloc(sizeof(int) * 10);//向内存申请40个字节空间
	if (p == NULL)//判断是否申请成功
		return 1;//失败直接放回
	for (int i = 0; i < 10; i++)//成功就正常使用
		*(p + i) = i;
	for (int i = 0; i < 10; i++)//打印
		printf("%d ", *(p + i));
	free(p);//释放
	p = NULL;//及时置空,防止出现野指针
	return 0;
}

我们可以通过调试观察free前后的区别:

free前:

free后:

3、calloc

(1)返回类型和参数:

void *calloc(size_t n,size_t  size);

返回类型为 void* ,所以和malloc一样想要什么类型的空间就进行强制转换类型即可,第一个参数为申请的个数,第二个参数为申请的类型的空间大小(单位为字节)

(2)作用:

申请一块连续的空间,并将空间的内容全部初始化为0,然后返回指向这块空间的指针

(3)注意:

a.在申请完之后我们还要判断是否成功申请
当申请失败时会返回NULL
当申请成功后就返回指向这块空间的的指针,这样可以正常使用这块空间了


b.如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器


c.与malloc的区别就是calloc会将申请的空间初始化,这样使用时更方便

(4)使用:

#include<stdio.h>
#include<stdlib.h>
int main() {
	int* p = (int*)calloc(10, sizeof(int));//申请
	if (p == NULL) {//判断
		printf("NULL");
		return 1;
	}
	//使用
	for (int i = 0; i < 10; i++)
		*(p + i) = i;
	for (int i = 0; i < 10; i++)
		printf("%d ", *(p + i));
	free(p);//同样的释放空间
	p = NULL;//置空
	return 0;
}

运行结果:

4、realloc

(1)返回类型和参数:

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

返回类型为 void* 所以和calloc一样想要什么类型就强制类型转换,第一个参数p为指向想要的改变的空间的 指针,第二参数为改变的大小(单位为字节)

(2)作用:

在原来的动态内存的空间上增大或者缩小,并返回改变后指向新的空间的指针

(3)注意:

a.开辟失败返回空指针
b.开辟的新的空间时有两种开辟的方式,第一种是我们原本的空间后面有足够的空间,那样直接在原来的空间后面扩容,第二种是我们原本的空间后面没有足够的空间,那样的话,系统就会在内存上找一块适合的空间重新开辟,并将原来空间的内容复杂过去,再将原来的空间销毁。
由于开辟的情况有两种,所以我们使用时要注意开辟空间失败这种情况
如:我们使用了第二种情况开辟空间、并直接用原来的指针去接收指向开辟的空间的指针,如果开辟失败的话返回NULL,这导致原来的数据会丢失。
所以为了解决这个隐患,我们可以重新定义一个指针来接收,判断不为空再将该指针赋给原来的指针。

图示:

 例子:

#include<stdio.h>
#include<stdlib.h>
int main() {
	//先用动态内存开辟一个空间
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
		return 1;
	for (int i = 0; i < 10; i++)
		*(p + i) = i;
	for (int i = 0; i < 10; i++)
		printf("%d ", *(p + i));
	printf("\n");
	//使用realloc增加空间
	int* pp = (int*)realloc(p, sizeof(int) * 15);//再申请一个指针变量来接收
	if (pp == NULL)
		return 1;
	else
		p = pp;//不为空再赋给原来的指针
	for (int i = 10; i < 15; i++)
		*(p + i) = i;
	for (int i = 10; i < 15; i++)
		printf("%d ", *(p + i));
	free(p);//最后不要忘记了释放并置空
	p = NULL;
	return 0;
}

运行结果:

三、常见的动态内存的错误

1、对NULL指针的解引用操作

void test()
 {
 int *p = (int *)malloc(INT_MAX/4);//开辟空间
 //这里我们不知道是否开辟成功
 *p = 20;//如果p的值是NULL,就会有问题
 free(p);
 p=NULL;
 }

这里的问题是不知道p为不为NULL,所以我们每次对动态内存进行操作时先对其进行判断看其是否位NULL。


改:

void test()
 {
 int *p = (int *)malloc(INT_MAX/4);//开辟空间
 if(p!=NULL)
 *p = 20;
 free(p);
 p=NULL;
 }

2 、对动态开辟空间的越界访问

void test()
 {
 int i = 0;
 int *p = (int *)malloc(10*sizeof(int));
 if(NULL == p)
 {
 exit(EXIT_FAILURE);
 }
 for(i=0; i<=10; i++)
 {
 *(p+i) = i;//当i是10的时候越界访问
 }
 free(p);
 p=NULL;
 }

3 、对非动态开辟内存使用free释放

 void test()
 {
 int a = 10;
 int *p = &a;
 free(p);//ok?
 }

4 、使用free释放⼀块动态开辟内存的⼀部分

void test()
 {
 int *p = (int *)malloc(100);
 p++;
 free(p);//p不再指向动态内存的起始位置
 }

这么做会导致内存空间的泄露

5 、对同⼀块动态内存多次释放

void test()
 {
 int *p = (int *)malloc(100);
 free(p);
 free(p);//重复释放
 }

程序会崩溃 

6、 动态开辟内存忘记释放(内存泄漏)

4.总结

到这里你已经基本掌握了动态内存的概念和使用方法,为接下来的柔性数组和数据结构的学习打下了基础,感谢大家的阅读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值