【C语言进阶】动态内存管理及柔性数组

2024-06-10 1163阅读

动态内存的开辟在C语言中相当重要的知识


1、为什么会存在动态内存分配

内存的开辟方式:

int a=20;//在栈空间上开辟4个字节

int arr[10];//在栈空间上开辟40个字节的连续空间

这种开辟空间的方式有两个特点:

1、开辟的空间大小是固定的

2、数组在声明的时候,必须指定数组长度,它需要的内存在编译时分配。

但是这种内存开辟的方式存在缺陷,比如我们在写通讯录管理系统时指定了100个元素,但当我们填入元素过多时空间会不够用,当联系人较少时,又会产生空间的浪费,所以我们可以用一种灵活的方式,用多少提供多少,这种方式即动态内存管理。

2、动态内存函数的介绍

2.1malloc和free

void * malloc(size_t  size);

· 如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。

· 返回类型为void*,所以malloc函数并不知道开辟空间的类型,具体在使用时侯使用者自己来决定。

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

free是专门用来做动态内存的释放和回收的:

void free(void* ptr)

· 如果参数ptr指向的空间不是动态开辟的,那么free的行为是未定义的

· 如果参数ptr是NULL指针,则函数什么事都不做

malloc函数与free函数的声明都在stdlib.h中

#include
#include
#include
#include
//malloc函数的使用
int main()
{
	int arr[10] = { 0 };
	//动态内存分配
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//说明开辟成功,使用内存
	int i = 0;
	for (i = 0; i  

注意:在使用malloc开辟空间时,使用完成一定要释放空间,否则可能导致内存泄漏。 

内存泄漏:是指程序动态分配内存后,未能及时释放这些内存,导致系统无法再为其他对象分配内存,或者可能导致系统内存耗尽的现象。

 2.2calloc

calloc函数也用来动态内存分配

void * calloc(size_t num,size_t size);

·​​​​​​​ 函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0.

·​​​​​​​ 与函数malloc的区别在于calloc会在返回地址之前把申请的空间的每个字节初始化为全0。

#include
#include
#include
#include
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	int i = 0;
	for (i = 0; i  

2.3realloc 

·​​​​​​​ realloc函数的出现让动态内存管理更加灵活

·​​​​​​​ 有时我们会发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,为了合理的分配内存,我们一定会对内存的大小做灵活地调整。那么realloc函数就可以做到对动态开辟内存大小的调整。

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

·​​​​​​​ ptr是要调整的内存地址

·​​​​​​​ size是调整后的新大小 

·​​​​​​​ 返回为调整之后的内存起始位置。

·​​​​​​​ 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新空间

·​​​​​​​ realloc在调整内存空间存在两种情况:

1、原有空间之后有足够大的空间;(直接追加)

2、原有空间之后没有足够大的空间;(会覆盖其他数据,所以开辟一块更大的空间,把原有数据拷贝过去)

#include
#include
#include
#include
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	int i = 0;
	for (i = 0; i  

3、常见的动态内存错误

3.1对NULL指针解引用操作

int main()

{

    int* p = (int*)malloc(40);//应该判断p是否为空指针,否则空间可能开辟失败

    *p = 20;

    return 0;

}

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

int main()

{

    int* p = (int*)malloc(40);

    if (p == NULL)

    {

        return 1;

    }

    int i = 0;

    for (i = 0; i n = 100; int i = 0; for (i = 0; i arr[i] = i; } for (i = 0; i arr[i]); }//柔性数组成员 struct S* ptr=(struct S*)realloc(ps, sizeof(struct S) + 80); if(ptr!=NULL) { ps=ptr; } //........ free(ps); ps=NULL;//ptr已经赋给ps了所以不用释放ptr,释放平时即可 return 0; }

struct S
{
	int n;
	int* arr;
};
int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		return 1;
	}
	ps->n = 100;
	ps->arr = (int*)malloc(40);
	if (ps->arr == NULL)
	{
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i arr[i] = i;
	}
	for (i = 0; i arr[i]);
	}
    //释放
    free(ps->arr);
    ps->arr=NULL;
    free(ps);
    ps=NULL;
	return 0;
}

6.3柔性数组的优势 

 第一个好处:方便内存释放

如果我们的代码是在一个给别人使用的函数中,你在里面做了二次内存分配,并把整个结构体返回用户。用户调用free可以释放结构体,但是用户并不知道这个结构体,但是用户并不知道这个结构体内的成员也需要free,所以不能指望用户来发现,所以,如果我们把结构体的内存以及成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存给释放掉。

第二个好处:有利于访问速度

连续的内存有利于提高访问速度,也有益于减少内存碎片 

 

 

 

 

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]