EP15:动态内存管理概述(c语言)malloc,calloc,realloc函数的介绍使用及柔性数组的介绍

本文主要是介绍EP15:动态内存管理概述(c语言)malloc,calloc,realloc函数的介绍使用及柔性数组的介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

序言:在c语言中,什么是动态内存

1.malloc函数简述

1.1free函数

1.2 malloc函数

2.calloc函数简述

3.对malloc函数与calloc函数进行总结

3.1 不同点 (函数参数上)

3.2 相同点(函数使用步骤上)

4.重要的realloc函数的简介与使用

4.1 对realloc函数的使用进行简述

4.2 以calloc函数进行开辟为例,扩容的公式

4.2.1calloc函数扩展内存空间的方式

5.柔性数组 

5.1 柔性数组概述

5.2 柔性数组的使用

ps:"->"操作符和"."(点)操作符的使用


如果学习方向是c++方向那么c语言有三个板块的知识是非常重要的. 1:指针 2:结构体 3;动态内存管理.

序言:在c语言中,什么是动态内存

C语言中的动态内存是指在程序运行时,根据需要动态地分配内存空间的一种内存管理方式。与静态内存相比,动态内存的大小和生命周期都可以在程序运行时动态地确定和调整,因此更加灵活。C语言中提供了四个函数:malloc、calloc、realloc和free,用于动态地分配和释放内存空间。其中,malloc和calloc用于分配内存空间,realloc用于调整已分配内存空间的大小,free用于释放已分配的内存空间。动态内存的使用需要引用头文件<stdio.h>或<malloc.h>。

在本篇文章中,我们将着重简述何为malloc函数,calloc函数,free,以及柔性数组

1.malloc函数简述

1.1free函数

C语言提供了另外⼀个函数free,专门是用来做动态内存的释放和回收的,函数原型如下:

 void free (void* ptr);

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

 但凡涉及到动态内存的开辟,释放空间这一步作为最后一步绝对是不可或缺的.

1.2 malloc函数

函数头文件: #include <stdlib.h>

函数参数: void* malloc (size_t size);

函数作用: 用于开辟动态内存

从代码示例看函数的具体使用方法

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)malloc(10 * sizeof(int));//1.动态内存的开辟(指针p所指向的是malloc函数开辟空间的起始地址)

     malloc():括号里面要写的是开辟多少大小的内存空间,一般用:任意整数*sizeof(整型)
    if (p == NULL)
    {
        perror("malloc");
        return 1;
    }


    int i = 0;
    for (i = 0; i < 10; i++)//2.对动态内存开辟的空间进行访问

    {
        *(p + i) = i;//使用指针p去访问malloc开辟的空间里面的元素,再解引用赋值给i
    }


    for (i = 0; i < 10; i++)//3.对开辟的空间中的内容进行打印
    {
        printf("%d ",i );//将刚才访问的空间里面存储的数据打印出来
    }


    free(p);//4.对开辟的空间进行回收
    p=NULL;
    return 0;
 }

为啥要这么写:  int* p = (int*)malloc(10 * sizeof(int));

解释:由函数参数 void* malloc (size_t size);可知malloc函数一个空指针类型的函数,但是由于空指针是不可以直接用于指针的运算的,所以我们要将它强制类型转换成我们想要的类型.

一点联想:这一点让我想起了qsort函数,感觉void*类型的指针都要进行这样的操作,可以在这里留意一下并且进

行相关的知识迁移以便日后遇到相似知识点的学习

2.calloc函数简述

总而言之,calloc函数的使用方法以及功能和malloc函数基本一致

函数头文件: #include <stdlib.h>

函数参数: void* calloc (size_t num, size_t size);

函数作用: 用于开辟动态内存

从代码示例看函数的具体使用方法

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    int* p = calloc(10, sizeof(int));//1.动态内存的开辟
    if (p == NULL)
    {
        perror("calloc");
        return 1;
    }


    int i = 0;//2.对动态内存开辟的空间进行访问
    for (i = 0; i < 10; i++)
    {
        *(p + i) = i;
    }


    for (i = 0; i < 10; i++)//3.对开辟的空间中的内容进行打印
    {
        printf("%d ", *(p + i) );
    }


    free(p);//4.对开辟的空间进行回收
    p = NULL;

    return 0;
}

3.对malloc函数与calloc函数进行总结

3.1 不同点 (函数参数上)

以上述两段代码为例

malloc函数是:int* p = (int*)malloc(10 * sizeof(int));

calloc函数是: int* p = (int*)calloc(10, sizeof(int));

观察上述两段代码,其实也没啥不同点,就是把函数括号里面我们想要开辟的空间的那个表达式把逗号改成了*号的区别嘛

3.2 相同点(函数使用步骤上)

首先要明确的是它们都要进行强制类型转换转换成我们想要的指针类型.

最重要的是,它们是使用步骤可以分成四步

1.动态内存的开辟

2.对动态内存开辟的空间进行访问

3.对开辟的空间中的内容进行打印

4.对开辟的空间进行释放

简化一下就是: 1.开辟 2.访问 3.使用 4.释放

这两个函数在这四个步骤上的写法是一摸一样,没有任何区别的.将此四部先后逻辑顺序理清并进行适当的记忆这两个动态内存函数便可以说是掌握了.

所以此二者等价

4.重要的realloc函数的简介与使用

4.1 对realloc函数的使用进行简述

 在本文的开头阐述过何为动态内存"...动态内存的大小和生命周期都可以在程序运行时动态地确定和调整,因此更加灵活。..."

故而没有realloc函数的介入很难把一段可正常运行的代码叫做"动态内存管理"

函数头文件: #include <stdlib.h>

函数参数: void* realloc (void* ptr, size_t size);

对函数参数的解释: void*ptr就是要进行扩展的对象,size_t size就是要将被扩展的扩展至管理员预期的空间

函数作用: 用于动态内存的扩展,要和malloc函数或者calloc函数进行联合使用

具体且形象的讲解realoc函数的作用:

realloc的作用就是将原本malloc和calloc开辟的空间扩大到多少
举个例子就是说如果malloc或calloc是一段单向路上不与起点重合的一个质点,calloc就是另外一个质点
用calloc质点到起点的距离减去malloc,calloc质点所在的距离便是calloc函数所追加的空间

如图


 

从代码示例看函数的具体使用方法

就是在malloc函数和calloc函数原先的使用步骤上加上扩容这一步

1.动态内存的开辟

2.对动态内存开辟的空间进行访问

3,对malloc函数或calloc函数开辟的空间进行扩容

4.对开辟的空间中的内容进行打印

5.对开辟的空间进行释放

总结下来就是 1.开辟 2.访问 3,扩容 4.使用 5.释放

4.2 以calloc函数进行开辟为例,扩容的公式

    int* ptr = (int*)realloc(p, 15 * sizeof(int));//扩展开辟空间
    if (p != NULL)
    {
        p = ptr;
    }
    else
    {
        perror("errno");
        return 1;
    } 

为什么这么写: int* ptr = (int*)realloc(p, 15 * sizeof(int));

这里便涉及到calloc函数扩展内存空间的方式了

4.2.1calloc函数扩展内存空间的方式

情况1:扩展失败,返回NULL,于是便有了 

 else
    {
        perror("errno");
        return 1;
    } 

情况2;扩展成功了,于是便有了

if (p != NULL)
    {
        p = ptr;
    }

扩展成功方式1:

在malloc函数或者calloc函数开辟好的空间后进行扩容,如果没有足够的空间进行扩大时候,此时的calloc函数会在堆区中重新选择一块大小满足需求的空间,同时将旧空间中的旧数据连同着一块拷过来,这也是为啥虽然上述代码  int* ptr = (int*)realloc(p, 15 * sizeof(int));虽然写的是15 * sizeof(int)实际上只是扩展了5*sizeof(int)大小的空间.然后释放就空间,同时返回新的空间.

扩展成功方式1:

若空间大小足够,则在已经开辟好的空间直接进行追加空间进行扩展,扩大空间后,直接返回就空间的起始地址.

4.3 从示例看realloc函数的具体用法 

根据上面总结的五个步骤来写:  1.开辟 2.访问 3,扩容 4.使用 5.释放

int main()// 开辟 访问   扩容 使用 释放
{
    //开辟
    int* p = (int*)calloc(10, sizeof(int));
    if (p == NULL)
    {
        perror("calloc");
        return 1;
    }

    //访问
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        *(p + i) = i;
    }

    //扩容
     int* ptr = (int*)realloc(p, 15 * sizeof(int));//扩展开辟空间
    if (p != NULL)
    {
        p = ptr;
    }
    else
    {
        perror("errno");
        return 1;
    }

    //使用
    for (i = 0; i < 10; i++)
    {
        printf("%d ", *(p + i) );
    }

    

    //释放
    free(p);
    p = NULL;

    return 0;
}

ps:其实realloc函数除了调整空间外,也可以实现和malloc或者realloc函数一样的功能

int*p=(int*)realloc(NULL,1o*sizeof(int));等价于malloc或者calloc函数,只是一般不这么去写.

5.柔性数组 

5.1 柔性数组概述

 C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。

结构体成员的特点

• 结构中的柔性数组成员前面必须至少一个其他成员。

• sizeof 返回的这种结构大小不包括柔性数组的内存。

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

5.2 柔性数组的使用

以malloc函数为例,柔性数组的使用依旧遵循上述的五个步骤,不同的是要先创建一个结构体

1. 开辟 2.访问 3.扩容 4.使用 5.释放

struct st
{
    char c;
    int n;
    int arr[0];
};


int main()
{
    struct st* ps = (struct st*)malloc(sizeof(struct st) + 10 * sizeof(int));//1.开辟
    //前面的sizeof(struct st)是计算此结构体本来的大小
    //后面的 10 * sizeof(int) 意思是将柔性数组成员开辟成多少大小的

    if (ps == NULL)
    {
        perror("malloc");
        return 1;
    }

    ps->c = 'w';//2.访问
    ps->n = 100;
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        ps->arr[i]=i;//也可以这么写 QUESTION2;点操作符和箭头操作符的区别,在那些情况下一般用什么的总结归纳
    }

    struct st* ptr = (struct st*)realloc(ps, sizeof(struct st) + 15 * sizeof(int));//3.扩容
    if (ptr != NULL) 
    {
        ps = ptr;
    }
    else
    {
        perror("realloc");
        return 1;
    }

    for (i = 0; i < 10; i++)//4.使用
    {
        printf("%d ", i);
    }
    printf("\n");
        printf("%c \n", ps->c);
        printf("%d \n", ps->n);


    free(ps);//5.释放
    ps = NULL;
    return 0;
}

ps:"->"操作符和"."(点)操作符的使用

1.点操作符的使用情况

直接使用

  struct st
{
    int a;
    int b;
};
int main1()
{
    struct st s = { .a = 10 , .b=20 };
    printf("%d %d ", s.a, s.b);
    return 0;
}

2.->的使用情况

由柔性数组的实例情况使用可知,在"结构体+指针"的情况下如果要使用指针对结构体中某一个成员变量进行访问,那么就是"指针->结构体成员变量名=程序员想要赋的值".

封面如下

 

这篇关于EP15:动态内存管理概述(c语言)malloc,calloc,realloc函数的介绍使用及柔性数组的介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/492392

相关文章

C语言中的浮点数存储详解

《C语言中的浮点数存储详解》:本文主要介绍C语言中的浮点数存储详解,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、首先明确一个概念2、接下来,讲解C语言中浮点型数存储的规则2.1、可以将上述公式分为两部分来看2.2、问:十进制小数0.5该如何存储?2.3 浮点

MySQL中COALESCE函数示例详解

《MySQL中COALESCE函数示例详解》COALESCE是一个功能强大且常用的SQL函数,主要用来处理NULL值和实现灵活的值选择策略,能够使查询逻辑更清晰、简洁,:本文主要介绍MySQL中C... 目录语法示例1. 替换 NULL 值2. 用于字段默认值3. 多列优先级4. 结合聚合函数注意事项总结C

Java使用Mail构建邮件功能的完整指南

《Java使用Mail构建邮件功能的完整指南》JavaMailAPI是一个功能强大的工具,它可以帮助开发者轻松实现邮件的发送与接收功能,本文将介绍如何使用JavaMail发送和接收邮件,希望对大家有所... 目录1、简述2、主要特点3、发送样例3.1 发送纯文本邮件3.2 发送 html 邮件3.3 发送带

使用DeepSeek搭建个人知识库(在笔记本电脑上)

《使用DeepSeek搭建个人知识库(在笔记本电脑上)》本文介绍了如何在笔记本电脑上使用DeepSeek和开源工具搭建个人知识库,通过安装DeepSeek和RAGFlow,并使用CherryStudi... 目录部署环境软件清单安装DeepSeek安装Cherry Studio安装RAGFlow设置知识库总

Python FastAPI入门安装使用

《PythonFastAPI入门安装使用》FastAPI是一个现代、快速的PythonWeb框架,用于构建API,它基于Python3.6+的类型提示特性,使得代码更加简洁且易于绶护,这篇文章主要介... 目录第一节:FastAPI入门一、FastAPI框架介绍什么是ASGI服务(WSGI)二、FastAP

Spring-AOP-ProceedingJoinPoint的使用详解

《Spring-AOP-ProceedingJoinPoint的使用详解》:本文主要介绍Spring-AOP-ProceedingJoinPoint的使用方式,具有很好的参考价值,希望对大家有所帮... 目录ProceedingJoinPoijsnt简介获取环绕通知方法的相关信息1.proceed()2.g

Maven pom.xml文件中build,plugin标签的使用小结

《Mavenpom.xml文件中build,plugin标签的使用小结》本文主要介绍了Mavenpom.xml文件中build,plugin标签的使用小结,文中通过示例代码介绍的非常详细,对大家的学... 目录<build> 标签Plugins插件<build> 标签<build> 标签是 pom.XML

JAVA SE包装类和泛型详细介绍及说明方法

《JAVASE包装类和泛型详细介绍及说明方法》:本文主要介绍JAVASE包装类和泛型的相关资料,包括基本数据类型与包装类的对应关系,以及装箱和拆箱的概念,并重点讲解了自动装箱和自动拆箱的机制,文... 目录1. 包装类1.1 基本数据类型和对应的包装类1.2 装箱和拆箱1.3 自动装箱和自动拆箱2. 泛型2

JAVA虚拟机中 -D, -X, -XX ,-server参数使用

《JAVA虚拟机中-D,-X,-XX,-server参数使用》本文主要介绍了JAVA虚拟机中-D,-X,-XX,-server参数使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录一、-D参数二、-X参数三、-XX参数总结:在Java开发过程中,对Java虚拟机(JVM)的启动参数进

Java中数组转换为列表的两种实现方式(超简单)

《Java中数组转换为列表的两种实现方式(超简单)》本文介绍了在Java中将数组转换为列表的两种常见方法使用Arrays.asList和Java8的StreamAPI,Arrays.asList方法简... 目录1. 使用Java Collections框架(Arrays.asList)1.1 示例代码1.