• 动态内存管理知识点


    动态内存管理

    为什么存在动态内存管理?

    int a=10;//在栈空间上面开辟四个字节
    char arr[10]= {0};----在栈空间上开辟10个字节的连续空间
    
    • 1
    • 2

    这是我们目前所掌握的开辟空间的方式

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

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

    这种固定开辟空间的方式就有了局限性,不方便后期进行空间上的维护

    动态内存函数

    #include 
    
    • 1

    malloc

    void* malloc (size_t size);
    
    • 1

    分配一个字节的内存块,返回指向该块开头的指针

    1、如果开辟成功,返回一个指向开辟空间的指针

    2、申请空间可能会申请失败,如果开辟失败,返回的是空指针,因此malloc的返回值一定要检查有效性

    3、返回值的类型是void*类型,所以malloc函数并不知道开辟空间的类型,具体在使用的时候由使用者来决定

    int arr[10]={0};
    int *p = (int *)malloc(40);
    if(p==NULL)
    {
    printf("%s\n",strerror(errno));
    return 1;----历史习惯:return 0是正常返回,return 1就是异常返回
    }
    ----当程序退出的时候,系统会自动回收内存空间,并不是说 内存空间就不回收了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    free

    void free (void* ptr);
    
    • 1

    free函数用来释放动态开辟的内存的

    1、如果不是动态开辟的内存,是不能够用free来释放的

    2、如果参数ptr是空指针,则函数什么事情都不会做

    free是释放动态开辟的内存,然而p还记得free以前内存的地址,但此时如果再访问p指向的内存地址,会造成对非法空间访问的问题,也就演变成了野指针的问题,所以,对于动态开辟的内存释放以后,置为空指针是必须的。

    calloc

    void* calloc (size_t num, size_t size);
    
    • 1
    函数是开辟num个大小为size的空间
    与malloc函数的区别就是calloc会在返回地址之前把申请到的空间的每个字节全部初始化为0
        也就是相当于calloc = malloc +memset
    
    • 1
    • 2
    • 3

    总结一句话,如果想要把申请到的内存空间初始化,就用calloc

    如果不想初始化,就用malloc

    realloc

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

    1、ptr为要调整的内存地址

    2、size为调整后的新大小

    3、返回值为调整之后的内存地址

    realloc函数调整内存空间的时候会有两种情况

    1、原有空间后面没有足够大的空间

    2、原有空间后面有足够大的空间1

    int *ptr= malloc(20);
    int *tmp =(int*) realloc(ptr,40);
    
    • 1
    • 2

    1、没有足够空间,在堆空间其他位置找一个足够大小的空间,将原数据拷贝到这40个字节的空间中,并返回新开辟空间的起始地址

    在这里插入图片描述

    2、有足够空间,直接在后面追加20个字节的空间

    在这里插入图片描述

    不能直接用ptr来接收,如果要开辟的空间过大,堆上没办法开辟足够大的空间,realloc函数就要返回空指针,那样ptr就变成了空指针,以前的空间也找不到了

    柔性数组

    在C99标准中,结构中的最后一个元素允许是未知大小的数组

    struct Stu
    {
    	int i;
    	int arr[0];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在结构体成员中,最后一个成员是未知大小的数组成员,并且在柔型数组前面至少有一个元素

    在计算大小的时候,只计算了柔性数组前面其他的成员大小

    柔性数组的特点:

    1、结构中的柔性数组前面至少有一个其他成员

    2、sizeof返回的结构大小并不会包括柔性数组的内存

    3、包含柔性数组成员的结构应该用malloc函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小

    🌰栗子:

    struct Stu s;----不这样创建,这样的话只能存放4个字节,没有给柔性数组开辟空间
    
    • 1

    柔性数组的使用

    int i=0;
    struct Stu* p = (struct Stu*)malloc(sizeof(struct Stu)+10*sizeof(int));
    if(p==NULL)
    {
        return 1;
    }
    p->i=100;
    for(i=0;i<100;i++)
    {
        p->arr[i] = i;
    }
    free(p);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    获得了100个整型元素的连续空间

    如何柔性?可以通过realloc来维护空间

    int i=0;
    struct Stu* p = (struct Stu*)malloc(sizeof(struct Stu)+10*sizeof(int));
    if(p==NULL)
    {
        return 1;
    }
    p->i=100;
    for(i=0;i<10;i++)
    {
        p->arr[i] = i;
    }
    struct Stu* ptr = (struct Stu*)realloc(p,sizeof(struct Stu)+80);----扩容
        if(p!=NULL)
        {
            ps=ptr;
            ptr=NULL;
        }
    free(ps);
    ps=NULL;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    那肯定会 有人问到了,为什么不能直接用int*类型的指针来维护一块动态内存空间呢?

    struct S
    {
    	int n;
    	int* arr;
    };
    
    int main()
    {
    	struct S*ps = (struct S*)malloc(sizeof(struct S));----因为动态开辟的内存是在堆区,所以我们把int类型的变量也创建在堆区
    	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 < 10; i++)
    	{
    		printf("%d ", ps->arr[i]);
    	}
    	//扩容
    	int*ptr = (int*)realloc(ps->arr, 80);
    	if (ptr == NULL)
    	{
    		return 1;
    	}
    	else
    	{  
    		ps->arr = ptr;//补充:如果扩容成功,这里要讲ptr的值赋值给ps->arr,空间依然由ps->arr维护
    	}
    	//使用
    	//释放
    	free(ps->arr);
    	free(ps);
    	ps = NULL;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    如果用int类型的指针开辟一块空间,第二种方法做了两次动态内存分配,在进行free释放结构体的时候,用户释放结构体,但不知道这个结构体中的成员也需要free,如果我们把结构体的内存以及成员需要的内存一次性分配好,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存释放掉。

    并且,多次进行malloc还会造成内存碎片化。

  • 相关阅读:
    【日常踩坑】解决 kex_exchange_identification 报错
    HDFS学习笔记(三):HDFS 分布式文件系统原理
    计算未来:微软眼中的人工智能
    安全性和合规性:保障企业数据的安全
    载20(S)-人参皂苷/细胞穿膜肽-单克隆抗体-载丝裂霉素白蛋白纳米微球的制备
    【C语言从入门到放弃 5】输入&输出,文件读写,预处理器和头文件详解
    如何利用大数据构建用户画像?
    浏览器的 5 种 Observer
    mermaid 之 (Flowchart) 流程图
    ElasticSearch(es)使用游标读取全部数据
  • 原文地址:https://blog.csdn.net/m0_66032294/article/details/126000736