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

相关文章

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版