动态内存管理

目录

前言:

一,malloc函数与free函数

1.1malloc函数基本内容

1.2实例分析malloc函数以及free函数

1.3free函数基本内容

二,calloc动态内存分配函数

2.1malloc函数基本情况

2.2调试观察calloc函数

三,realloc函数

3.1realloc函数的由来

3.2realloc的基础知识

3.3realloc函数代码实现

四,柔性数组


前言:

我们知道,对于C/C++程序内存分配来说:大致可以分为三个大区:栈区,堆区,静态区

栈区:栈内存分配运算内置于处理器的指令集中,效率很高,但是内存容量很小。所以在栈区中,一般存放的都是临时变量,比如函数形参,局部变量等,出了其作用域范围即被销毁。

堆区:即为动态内存分配的区域,这里的内存一般由程序员释放,否则操作系统会自动回收。分配方式类似于链表。

静态区:静态区也称为数据段,存放全局变量,静态数据(static修饰的全局变量,以及static修饰的局部变量)。在程序结束后,由系统释放。

那么我们知道了,在堆区中:需要程序员来使用命令管理及回收,否则可能发生内存泄漏等一系列问题。所以本文将为你带来有关动态内存分配的内容。

一,malloc函数与free函数

1.1malloc函数基本内容

对于动态内存分配来说,malloc函数是较为基础的一个内存分配函数,其格式如下:

基本格式:void * malloc(size_t size)

可以看到,对于malloc函数来说,其参数只有一个,即为无符号整型的 size ,表示要开辟的内存大小;其返回值为无符号的指针类型,因为并不确定用户需要什么样的数据类型,所以这里设为无符号类型,方便用户强制类型转换为自己想要的类型。

以下为对malloc函数的几点说明:

1.当malloc函数开辟空间成功时,会返回开辟成功空间的首地址。如果开辟空间失败,会返回一个空指针。

2.所以当malloc函数开辟完空间后,需要判断其是否为空,只有当不为空时,才能做后续的操作。

3.当size为 0 时,即不开辟空间时,C语言标准未做具体规定,所以是由编译器决定的。 

1.2实例分析malloc函数以及free函数

那么了解完对于malloc函数的基本操作之后,我们通过一个具体的例子来深究其用法:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int num = 4;
	int* ptr = (int *)malloc(num*sizeof(int));
	if (NULL == ptr)
	{
		printf("分配空间失败\n");
	}
	int i = 0;
	for (i = 0; i < num; i++)
	{
		*ptr = i;
		printf("%d ",*ptr);
		ptr++;
		//即打印完事ptr再往后走一位,这样就不会出现打印错误了。
	} 
	free(ptr);//释放分配的空间。当ptr指向的空间为NULL或者不是动态分配的,free函数基本是什么也不做。
	ptr = NULL;//将该指针置为空,避免出现野指针
	return 0;
}

可以看到,首先我们需要将该函数强制类型转换为我们需要的整型,然后在malloc分配空间结束后,判断其是否为空,如果不为空,再进行我们需要的操作,这里小编是将分配的num个整型初始化为从 0 开始的相应的数字。

1.3free函数基本内容

然后这里涉及到了一个动态内存开辟,不可或缺的释放函数free :

基本格式:void free(void * ptr)

可以看到,对于free函数来说, 参数为一个无符号指针,因为free函数,就是释放动态分配的内存,所以该参数为指向刚刚分配的内存的首地址的指针。所以该函数的功能就是释放malloc函数动态内存分配的空间。

同时,一定不能忘记的是,在 free 函数释放完成之后,需要将该指针置为空。因为当内存被释放返回系统了,如果指针还记着该地址,如果之后再次访问,会出现非法访问内存的错误。所以对于free函数来说,其实包含两部分。

 那么看到这里,相信小伙伴们应该大概明白了,malloc函数以及free函数对于动态内存开辟的重要性了,但是小编这里仍旧需要补充几个不常见,但不能缺少的内容:

1.当free函数的指针指向的空间不是动态内存函数开辟的空间时,在C语言标准中是未定义的。

2.当free函数指向的空间为空时,free函数什么都不会做,所以一般我们要避免这类情况的发生。

好的,那么对于malloc函数以及free函数的了解到这里就结束了。因为考虑到现实中对于动态内存开辟相对来说要求是比较多的,所以接下来我们来了解一下calloc函数以及realloc函数。

二,calloc动态内存分配函数

2.1malloc函数基本情况

 对与calloc函数来说,只是在malloc函数的基础上增加了一个参数,所以大概内容是差不多的:

基本格式:void *calloc(size_ t num,size_t size)

即意为将分配的num个size大的空间全部初始化为 0,除此之外,与malloc函数没有任何区别,但是一般情况下,对于将内容初始化为 0 这一操作来说,大多数时候是不需要的,所以calloc函数应用是没有那么广泛的。

2.2调试观察calloc函数

所以这里我们通过调试观察一下其初始化为 0  的操作:

        如图所示,我们可以看到我们想要用calloc函数分配 4 个整型大小的空间,然后将其全部初始化为 0,这就是calloc函数为我们带来的便利之处。

三,realloc函数

3.1realloc函数的由来

因为每次动态内存分配不可能一次就完全分配够,如果一次分配很大空间的话,那么将跟直接分配空间一样了,那么就没必要了,所以当我们每次分配空间不够用时,我们考虑到追加分配,也即在原空间的基础上,追加分配一定的空间,也即实现了“用多少拿多少”的理念。

3.2realloc的基础知识

基本格式:void *realloc(void * ptr,size_t size)

 首先,realloc函数第一个参数即为原空间的起始地址的指针,然后第二个参数为追加空间或者重新开辟的空间的大小,所以就是新空间的大小。

对于第二个参数:因为我们不能保证原空间后面有足够的空间提供给我们去开辟。所以当原空间后面不够我们开辟时,realloc函数会在堆区上重新开辟一块空间,同时,会将原空间的内容全部拷贝到新空间。如果后面空间足够去追加时,将不会开辟新空间,而是在原空间后面追加我们需要的空间大小。

最后,返回值为更新后的空间的起始地址。

3.3realloc函数代码实现

#include<stdio.h>
#include<stdlib.h>
int main()
{
	//1.开辟空间
	int* ptr = (int*)malloc(100*sizeof(int));
	if (NULL == ptr)
	{
		printf("开辟空间失败\n");
	}
	//使用……
	//空间不够了
	//2.重新开辟空间
	int *p = (int*)realloc(ptr, 200*sizeof(int));
    //判断是否可以开辟成功
	if (p == NULL)
	{
		printf("开辟空间失败\n");
	}
	else
	{
		ptr = p;
		//使用……
	}
	//3.最后释放空间
	free(p);
	p = NULL;
	return 0;
}

这里,我们在malloc函数开辟的空间不够时,用realloc函数进行追加空间,当然因为这里的例子很简单,我们并没有将其用到具体的实例中,所以看着比较简单。但是在具体的应用中我们能想得到的是会一次又一次的增加空间大小,所以会在循环中,或者多次的函数调用中增加空间的大小。

当然,对于这三个函数来说,都不能缺少的是free函数的两步走释放空间。

如果一旦忘记释放或者忘记将释放后的指针置为空,会产生内存泄漏,非法访问等一系列问题。

四,柔性数组

可能对于柔性数组大家听得很少,那么今天小编给大家带来对于柔性数组的介绍:

1.C99中,结构中最后一个成员为大小未知的数组时,这个就叫做柔性数组成员。

2.柔性数组成员之前,必须得有一个其他类型的成员。

3.计算柔性数组成员所在的结构时,不计算数组的大小。

4.包含柔型数组成员的结构一般用malloc等动态内存分配空间,所以在分配时,一般会大于结构的大小,因为这样才能让柔性数组成员在以后可以有空间去存放内容。

这里我们用结构体为大家演示柔性数组成员的作用:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct type
{
	int i;
	int a[];
};
int main()
{
	struct type*p = (struct type*)malloc(sizeof(struct type) + 20 * sizeof(int));
	if (p == NULL)
	{
		printf("%s\n",strerror(errno));
	}
	else
	{
        //使用……
		//空间不够,追加
		struct type* ptr = (struct type*)realloc(p, sizeof(struct type) + 10 * sizeof(int));
		if (ptr == NULL)
		{
			printf("追加空间失败\n");
		}
		else
		{
			p = ptr;
			//使用……
		}
		//只需要释放一次,即释放新空间的地址
		free(ptr);
		ptr = NULL;
	}
	return 0;
}

可以看到,整个过程中当我们使用柔性数组时,在开辟空间的时候,是将成员 i 和成员柔型数组放在一起开辟了一块空间,尽管中间我们空间不够了,再次开辟了,我们在最后用完释放的时候,依然只需要释放一个指针,因为我们开辟的空间最后只有一个指针。

那么我们想到,如果将该柔型数组换为指针变量,即如下图所示:

 此时我们看到,如果要开辟空间,不仅仅要给结构体整体开辟空间,同时因为第二个成员是整型指针,所以也需要开辟一块空间,否则该成员将无法使用。那么这里我们将用到两个指针,最后在释放的时候,是需要释放两个指针的,因为两次不同的空间开辟,不像上面柔型数组不管怎么开辟空间,最后只有一个地址。

所以,对比来说,如果你的这段代码将在别人的程序中使用,可能他们看到的只是最后的那个结构体的地址的指针,但是看不到该整型指针成员的地址,所以有可能发生内存泄漏!那么相对来说,我们觉得柔性数组是更好地选择。

好的,本文到此就结束啦,如有问题,还请留言哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值