动态内存管理,malloc和calloc以及realloc函数用法

目录

一.malloc函数的介绍

malloc的用法

举个例子

注意点 

浅谈数据结构里的动态分配空间

二.calloc函数的介绍

三.realloc函数的介绍

四.柔性数组的介绍

为什么有些时候动态内存函数头文件是malloc.h,有些时候却是stdlib.h


一.malloc函数的介绍

malloc其实就是动态开辟空间,其实就是让程序员自己去决定开辟多大空间 

int  a=5,在栈空间上开辟4个字节去存变量a,char b=1在栈空间上开辟1个字节去存变量b,包括数组char arr[10]以上空间都是固定的,这种开辟空间是编译器自动完成分配的,在程序运行之前就已经分配好了,编译器负责将源代码翻译成机器语言,并确定变量在内存中的存储方式和位置。操作系统负责管理内存并为程序分配空间,但编译器决定了变量在内存中的布局和大小。

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间⼤⼩在程序运⾏的时候才能知
道,那数组的编译时开辟空间的⽅式就不能满⾜了。
C语⾔引⼊了动态内存开辟,让程序员⾃⼰可以申请和释放空间,就⽐较灵活了。

malloc的用法

void * malloc (size_t size)

这个函数向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针。
• 如果开辟成功,则返回⼀个指向开辟好空间的指针。
• 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。
• 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃
⼰来决定。
• 如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器。

举个例子

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{int arr[10] = { 5,4,6,3,4,5,7,8,9,10 };int* ptr = (int*)malloc(20 * sizeof(int));if (ptr != NULL) {memcpy(ptr, arr, 10 * sizeof(int)); // 将原数组的内容拷贝到新分配的内存中for (int i = 0; i < 10; i++){printf("%d ", *(ptr + i));}}free(ptr);//结束要把指针指向的空间释放,将指针置为空ptr = NULL;
}

 

(int*)malloc(20 * sizeof(int)),动态分配20个int类型的空间,最后返回值是指向这片空间的指针,我这片空间是准备用来存整型的(int),所以要强制转换成(int *),用整型指针int *ptr来接收

同样也可以写成这样(int *)malloc(80),给一个具体的要开辟空间的大小数字

动态内存题目

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void Get(char* p)
{p = (char*)malloc(100);
}
void Test(void)
{char* str = NULL;Get(str);strcpy(str, "hello world");printf(str);
}

本意是在函数Get中给str分配空间,然后把字符串“hello world”复制给它

结果是直接报错,运行不起来,为什么呢。

str是个指针,本来应该放置的是它指向空间的地址,但是此时置为空指针,所以传过去用指针形式的p去接收。但是接收到的是str里面的值,而不是str指向地址,此时p里面的值也为NULL。

 

再然后开辟了一个一百字节大小的空间,并把这个地址赋值给了p

再然后问题就来了,此时str其实是传值传参,形参是实参的一份临时拷贝,形参的改变不会影响实参,str不会去指向开辟的那个空间的地址,str里面依旧为NULL,p出了函数这个作用域就会销毁了,它只能在作用域内使用

所以第一个改进方案来了,形参不是只能在它这个作用域里有效吗,那么我直接在作用域里完成strcpy操作不就行了,p是实打实保存的是指向我们开辟空间的地址,所以在p相关的函数里完成剩下的操作就行

第二种解决方案,str最后因为函数作用域以及传值传参的影响,p的值改变不会影响实参str,但是return过去的值我可以用str接收到啊,直接把p此时的值(此时p内部是保存开辟的100空间的地址)return回去,让str接收依旧能完成接下来的操作,相当于现在str内部保存的不是NULL而是开辟100字节的空间的地址了

第三种解决方案,既然传值传参是临时拷贝,形参的改变不会影响实参,那么 有没有什么办法改变实参呢。传地址传参过去,直接解引用操作不就是在原来的地方操作吗,但是需要注意的是此时str是个指针变量,它取地址传过去要用二级指针接收

 别忘了要手动用free释放开辟的空间,同时把指针置为空

注意点 

1.使用malloc分配空间一定要先判断接收的指针是否为空,然后再去使用。这是因为malloc函数分配空间不是百分百能成功的,如果分配空间太大,而系统原有的空间不足,这时malloc函数将返回NULL。所以为了确保程序的健壮性,我们通常会在使用malloc函数分配内存空间后,先检查返回的指针是否为NULL

2.在使用完了malloc函数末尾一定要将这片空间释放掉,malloc函数分配的空间不会自动释放,它将一直占用系统资源,直到程序结束才会被系统回收。忘记释放不再使用的动态开辟的空间会造成内存泄漏

3.free释放完空间后,一定要将接收的指针置为空,否则的话该指针会变成野指针。如果我们在使用已经释放的内存空间的指针,可能会导致程序崩溃或者产生不可预期的结果。

浅谈数据结构里的动态分配空间

#include<stdio.h>
typedef char ElemType;
#define MaxSize 50
#include<stdlib.h>
typedef struct
{ElemType data[MaxSize];//存放线性表中的元素int length;//存放线性表的长度
}SqList;//顺序表的类型
void CreateList(SqList*& L, ElemType a[], int n)
{int i = 0, k = 0;L = (SqList*)malloc(sizeof(SqList));while (i < n){L->data[k] = a[i];k++;i++;}L->length = k;
}

以上常见的顺序表的声明和创建顺序表的函数

typedef struct
{
    ElemType data[MaxSize];//存放线性表中的元素
    int length;//存放线性表的长度
}SqList;//顺序表的类型

正常来讲,结构体末尾应该是结构体变量,但是用了typedef将结构体起个小名为了SqList,所以SqList是个类型名,意思是这个顺序表类型名叫SqList

typedef char ElemType;也是这个意思,把char类型取个小名ElemType,但是注意小名能用,大名同样也能用,都是指同一个东西

void CreateList(SqList*& L, ElemType a[], int n)    这个SqList *L其实是结构体指针,为什么会写得这么奇怪呢SqList*& L,这个&不是取地址的意思,而是引用的意思。c语言里传值传参形参的改变不会影响实参,通过引用可以在函数内部修改指针L所指向的结构体,而不是仅仅修改指针本身的值。但是需要注意的是c语言没有引用,引用是c++里面的,所以有些纯c语言编译器可能会报错。

 同样纯c语言编译器中,可以通过用二级指针SqList **L接收L这个指针变量的地址,这时也可以在函数内部修改指针L所指向的结构体

 (SqList*)malloc(sizeof(SqList));此时是结构体类型,所以要强制转换成结构体指针类型,然后才能用结构体指针L去接收。L=(SqList *)malloc(sizeof(SqList))意思就是分配一个空间去存顺序表,空间大小为结构体大小

二.calloc函数的介绍

这个函数其实和malloc函数差不多,都是动态分配空间,区别在于malloc开辟了空间,这个空间里的值它不会给你初始化,里面的值一开始都是随机值。而calloc它给你开辟空间的同时,还会帮你初始化为0

可以很明显的看出,malloc函数开辟的空间除了我放的五个值,其他的都是随机值 

calloc函数你不指定值,它默认初始化为0

 

malloc函数参数只有一个 ,纯数字也可以,例如(强制转换的类型)malloc(100)

而calloc函数参数有两个,(强制转换的类型)calloc(要开辟多少个这样尺寸的元素,要转换类型的尺寸)。   例如(int *)calloc(10,sizeof(int))意思就是开辟10个int类型的空间

三.realloc函数的介绍

有时会我们发现过去申请的空间太⼩了,有时候我们⼜会觉得申请的空间过⼤了,那为了合理的时
候内存,我们⼀定会对内存的⼤⼩做灵活的调整。那 realloc 函数就可以做到对动态开辟内存⼤
⼩的调整

说白了,realloc就是对用malloc函数和calloc函数开辟空间大小的修改

void* realloc (void* ptr, size_t size)

ptr 是要调整的内存地址
• size 调整之后新⼤⼩
• 返回值为调整之后的内存起始地址,用指针接收
• 这个函数调整原内存空间⼤⼩的基础上,还会将原来内存中的数据移动到新的空间。
 

#include<stdio.h>
#include<stdlib.h>int main()
{int* p = (int*)malloc( 10*sizeof(int));//开辟一个可以存10个int类型的空间if (p != NULL){for (int i = 0; i < 5; i++)//只放五个值来观察一下剩下的初始值{*(p + i) = i;}for (int i = 0; i < 10; i++)//现在看看所有的值的情况{printf("%d ", *(p + i));}}int* pre = (int*)realloc(p, 20 * sizeof(int));if (pre != NULL){p = pre;}else{printf("出错了");}
}

以上代码就是malloc函数已经分配了10个int类型空间基础上再用realloc函数扩容至20个整型的情况

realloc扩容有两种情况,在malloc函数扩容空间的后面再加上需要扩容的空间,如果后面空间足够没有被别的数据占据的话,会直接在后面增加需要扩容的空间,同时返回的是之前malloc函数开辟空间的地址。

第二种情况,如果后面扩容的空间不够,已经被别的数据用了,那么realloc函数会在堆区另外找一个合适大小的空间去使用。会把原先malloc开辟空间的数据直接拷贝过来,然后释放删除原先的那块空间,返回的是在堆区新找空间的地址

需要注意的是虽然是扩容,但是realloc函数也可能扩容失败返回NULL ,如果用记住了malloc地址的p指针直接去接收realloc扩容后空间的地址,此时扩容失败返回NULL,会直接失去原先malloc函数中已经保存的数据。所以要先用pre判断是否为空,再然后把记住新空间地址的指针变量pre赋值给p

四.柔性数组的介绍

柔性数组是指结构中最后一个元素允许是未知大小的数组,柔性数组前面必须至少一个其它成员。具体表现形式如下

struct stu
{int i;int arr[];//也可以int arr[0];
};

一开始sizeof这个结构体是不会把这个数组加进去计算内存对齐的,结构体的大小只是柔性数组前面成员的大小

只有int类型成员的大小 

在使用这个数组之前要采用malloc函数给这个数组开辟至少大于结构体大小的空间

开辟方法可以选择malloc函数开辟结构体字节大小的基础上在加上需要给柔性数组开辟多大的空间的值,这样就保证了分配给柔性数组的大小大于结构体的大小,适应柔性数组预期大小

#include<stdio.h>
#include<stdlib.h>
struct stu
{int i;int arr[];//也可以int arr[0];
};
int main()
{struct stu* pre = (struct stu*)malloc(sizeof(stu) + 100 * sizeof(int));pre->i = 200;//给int i赋初始值200if (pre != NULL){for (int j = 0; j < 100; j++){pre->arr[j] = j;//给柔性数组赋值,检验一下,是否开辟空间成功printf("%d ", pre->arr[j]);}free(pre);pre = NULL;}
}

此时再来看一下这个结构体的大小

 

为什么依旧是4呢,这是因为malloc函数是在程序运行时动态分配的 ,而sizeof计算大小是在编译时计算的,所以无法包括柔性数组的大小。要计算柔性数组的大小,需要单独进行计算或者使用其他方法来获取其大小。意思计算sizeof先计算了结构体的大小,然后柔性数组才被分配了空间大小

第二种开辟方法,先单独给结构体开辟空间,然后再给柔性数组开辟空间

 第⼀个方法好处是⽅便内存释放
如果我们的代码是在⼀个给别⼈⽤的函数中,你在⾥⾯做了⼆次内存分配,并把整个结构体返回给用户。用户调⽤free可以释放结构体,但是⽤⼾并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存⼀次性分配好了,并返回给用户⼀个结构体指针,⽤⼾做⼀次free就可以把所有的内存也给释放掉。
第⼆个方法的好处是:这样有利于访问速度连续的内存有益于提⾼访问速度,也有益于减少内存碎片

为什么有些时候动态内存函数头文件是malloc.h,有些时候却是stdlib.h

据C语言标准,malloc.h不是标准C头文件,而是在早期的一些C编译器中使用的头文件。而stdlib.h是标准C头文件,包含了malloc函数的声明以及其他一些常用函数的声明。

在现代的C编译器中,通常推荐使用stdlib.h而不是malloc.h,因为stdlib.h是标准C头文件,可以在所有符合C标准的编译器中使用,而malloc.h可能不是所有编译器都支持。

因此,尽管在某些旧的代码中可能会看到使用malloc.h的情况,但在新的开发中,应该优先选择使用stdlib.h来包含malloc函数的声明。

虽然malloc.h名字没有包含realloc和calloc,但是其实这两个函数也能用这个头文件。

数据结构课本上有些时候还是使用malloc.h头文件来使用动态内存函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/224279.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

老卫带你学---leetcode刷题(29. 两数相除)

29. 两数相除 问题 给你两个整数&#xff0c;被除数 dividend 和除数 divisor。将两数相除&#xff0c;要求 不使用 乘法、除法和取余运算。 整数除法应该向零截断&#xff0c;也就是截去&#xff08;truncate&#xff09;其小数部分。例如&#xff0c;8.345 将被截断为 8 &…

云原生之深入解析Kubernetes中服务的性能

一、Pyroscope 简介 ① 什么是 Pyroscope&#xff1f; 开发人员通常需要查看生产应用程序中的性能瓶颈以确定问题的原因&#xff0c;为此通常需要可以通过日志和代码工具收集的信息。不幸的是&#xff0c;这种方法通常很耗时&#xff0c;并且不能提供有关潜在问题的足够详细信…

论文解读:Medical Transformer论文创新点解读

这篇文章其实就是基于Axial-DeepLab: Stand-Alone Axial-Attention forPanoptic Segmentation论文上进行的一些小创新 Stand-Alone Axial-Attention forPanoptic Segmentation论文解读&#xff1a; 论文解读&#xff1a;Axial-DeepLab: Stand-Alone Axial-Attention forPanop…

【TES720D-KIT】基于国内某厂商FMQL20S400全国产化ARM开发套件(核心板+底板)

板卡概述 TES720D-KIT是专门针对我司TES720D&#xff08;基于国内某厂商FMQL20S400的全国产化ARM核心板&#xff09;的一套开发套件&#xff0c;它包含1个TES720D核心板&#xff0c;加上一个TES720D-EXT扩展底板。 FMQL20S400是国内某厂商电子研制的全可编程融合芯片&#xf…

使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题

文章目录 一、 篇头二、 操作步骤2.1 编译AOSP AS工程文件2.2 将AOSP导入Android Studio2.3 切到Project试图2.4 等待index结束2.5 下载缺失的JDK 1.82.6 导入完成 三、 导入AS的好处3.1 本文案例演示源码编译错误AS对比同文件其余地方的调用AS错误提示依赖AS做错误修正 一、 篇…

逆向经历回顾总结

逆向经历回顾总结 一、前言 将自己的逆向经验做个总结&#xff0c;希望新手对逆向大方向能快速了解。高手有啥不一样的经验也可以讨论讨论。 二、个人经历 本人入行逆向全因一部韩剧“幽灵”&#xff0c;里面涉及渗透、病毒分析、取证的攻防对抗&#xff0c;我觉得对新手来说…

图片转HTML-screenshot-to-code

Github地址 https://github.com/abi/screenshot-to-code 在线站 Screenshot to Code 简介 这是一个基于GPT4开发的一个工具站&#xff0c;它可以基于截图生成站点代码&#xff0c;生成速度快且准确。

SpringBoot 究竟是如何跑起来的

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《SpringBoot》。&#x1f3af;&#x1f3af; &…

【MySQL 索引】InooDB 索引实现

1 索引定义 维基百科对数据库索引的定义: 数据库索引是数据库管理系统&#xff08;DBMS&#xff09;中的一个排序数据结构, 以协助快速查询和更新数据库表中的数据。 MongoDB对索引的定义: 索引是一种特殊的数据结构, 以有序和便于遍历的形式存储数据集合中特定字段或一组字段…

基于ssm服装定制系统源码和论文

idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 环境&#xff1a; jdk8 tomcat8.5 开发技术 ssm 基于ssm服装定制系统源码和论文751 1.1项目研究的背景 困扰管理层的许多问题当中,服装定制将是广大用户们不可忽视的一块。但是管理好服装定制又面临很多麻…

EasyV易知微助力智慧城市未来趋势发展——数字孪生城市

“智慧城市的未来趋势就是数字孪生”——《基于数字孪生的智慧城市》 城市数字化管理、智慧城市和数字孪生城市的发展是相互促进、逐步深化的过程。 城市数字化管理作为起点&#xff0c;奠定了信息化、数据化的基础&#xff1b;而智慧城市则将数字城市管理进一步升级&#xff…

RabbitMQ死信队列详解

什么是死信队列 由于特定的**原因导致 Queue 中的某些消息无法被消费&#xff0c;**这类消费异常的数据将会保存在死信队列中防止消息丢失&#xff0c;例如用户在商城下单成功并点击支付后&#xff0c;在指定时间未支付时的订单自动失效死信队列只不过是绑定在死信交换机上的队…

Springboot集成支付宝支付---完整详细步骤

网页操作步骤 1.进入支付宝开发平台—沙箱环境 使用开发者账号登录开放平台控制平台 2.点击沙箱进入沙箱环境 说明&#xff1a;沙箱环境支持的产品&#xff0c;可以在沙箱控制台 沙箱应用 > 产品列表 中查看。 3.进入沙箱&#xff0c;配置接口加签方式 在沙箱进行调试前…

Python (十) operator

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

找不到vcomp100.dll,无法继续执行代码怎么解决

在计算机编程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“找不到vcomp100.dll&#xff0c;无法继续执行代码”。这个错误通常出现在使用Visual Studio进行C开发时&#xff0c;它表示程序无法找到vcomp100.dll文件。vcomp100.dll是Visual C 2015 Redist…

认识产品经理以及Axure简单安装与入门

目录 一.认识产品经理 1.1.项目团队 1.2.概述 1.3.认识产品经理 1.4.产品经理工作范围 1.5.产品经理工作流程 1.6.产品经理的职责 1.7.产品经理的分类 1.8.产品经理能力要求 1.9.产品工具 1.10.产品体验报告 二.Axure简介 三.应用场景 四.安装与汉化 4.1.安装 4…

指纹浏览器有什么用?AdsPower 指纹浏览器都有哪些优势?

说到指纹浏览器&#xff0c;各位跨境卖家肯定都不陌生&#xff0c;指纹浏览器已经成为跨境电商不可或缺的有力工具&#xff0c;那么它具体有什么作用呢&#xff1f;如今市场上指纹浏览器品牌琳琅满目&#xff0c;东哥有没有什么推荐呢&#xff1f;在这里&#xff0c;东哥将为大…

matter模组有无源测试事例

测试一款matter模组的硬件性能 1.1 天线阻抗、电压驻波比测试 主要测试&#xff1a;PCB板载天线设计效率及板材PCB铜面的平整度等 1.2 模组有源数据测试 主要测试&#xff1a;模组的阻抗匹配、频偏等情况 1.3 模组传输能量精度 主要测试&#xff1a;矢量误差等数据 1.4 模…

Unity2023.3(Unity6)版本开始将可以发布WebGPU

翻译一段官网上的话&#xff1a; 利用Unity 2023.3(正式发布时应该称为Unity6)中最新的WebGPU图形API集成&#xff0c;尝试最大限度的提升您的网络游戏的真实感。 通过与谷歌的战略合作&#xff0c;Unity实时3D平台的强大的图形功能现在为图形丰富的网络游戏进行微调&#xff0…

提升英语学习效率,尽在Eudic欧路词典 for Mac

Eudic欧路词典 for Mac是一款专为英语学习者打造的强大工具。无论您是初学者还是高级学习者&#xff0c;这款词典都能满足您的需求。 首先&#xff0c;Eudic欧路词典 for Mac具备丰富的词库&#xff0c;涵盖了各个领域的单词和释义。您可以轻松查询并学习单词的意思、用法和例…