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

相关文章

Conda与Python venv虚拟环境的区别与使用方法详解

《Conda与Pythonvenv虚拟环境的区别与使用方法详解》随着Python社区的成长,虚拟环境的概念和技术也在不断发展,:本文主要介绍Conda与Pythonvenv虚拟环境的区别与使用... 目录前言一、Conda 与 python venv 的核心区别1. Conda 的特点2. Python v

Spring Boot中WebSocket常用使用方法详解

《SpringBoot中WebSocket常用使用方法详解》本文从WebSocket的基础概念出发,详细介绍了SpringBoot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消... 目录一、WebSocket基础概念1.1 什么是WebSocket1.2 WebSocket与HTTP

C#中Guid类使用小结

《C#中Guid类使用小结》本文主要介绍了C#中Guid类用于生成和操作128位的唯一标识符,用于数据库主键及分布式系统,支持通过NewGuid、Parse等方法生成,感兴趣的可以了解一下... 目录前言一、什么是 Guid二、生成 Guid1. 使用 Guid.NewGuid() 方法2. 从字符串创建

Knife4j+Axios+Redis前后端分离架构下的 API 管理与会话方案(最新推荐)

《Knife4j+Axios+Redis前后端分离架构下的API管理与会话方案(最新推荐)》本文主要介绍了Swagger与Knife4j的配置要点、前后端对接方法以及分布式Session实现原理,... 目录一、Swagger 与 Knife4j 的深度理解及配置要点Knife4j 配置关键要点1.Spri

Python使用python-can实现合并BLF文件

《Python使用python-can实现合并BLF文件》python-can库是Python生态中专注于CAN总线通信与数据处理的强大工具,本文将使用python-can为BLF文件合并提供高效灵活... 目录一、python-can 库:CAN 数据处理的利器二、BLF 文件合并核心代码解析1. 基础合

Python使用OpenCV实现获取视频时长的小工具

《Python使用OpenCV实现获取视频时长的小工具》在处理视频数据时,获取视频的时长是一项常见且基础的需求,本文将详细介绍如何使用Python和OpenCV获取视频时长,并对每一行代码进行深入解析... 目录一、代码实现二、代码解析1. 导入 OpenCV 库2. 定义获取视频时长的函数3. 打开视频文

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化

MySQL 中的 CAST 函数详解及常见用法

《MySQL中的CAST函数详解及常见用法》CAST函数是MySQL中用于数据类型转换的重要函数,它允许你将一个值从一种数据类型转换为另一种数据类型,本文给大家介绍MySQL中的CAST... 目录mysql 中的 CAST 函数详解一、基本语法二、支持的数据类型三、常见用法示例1. 字符串转数字2. 数字

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

Python内置函数之classmethod函数使用详解

《Python内置函数之classmethod函数使用详解》:本文主要介绍Python内置函数之classmethod函数使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 类方法定义与基本语法2. 类方法 vs 实例方法 vs 静态方法3. 核心特性与用法(1编程客