动态内存管理,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.rhkb.cn/news/217871.html

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

相关文章

Docker, Docker-compose部署Sonarqube

参考文档 镜像地址: https://hub.docker.com/_/sonarqube/tags Docker部署文档地址 Installing from Docker | SonarQube Docs Docker-compose文档部署地址&#xff1a; Installing from Docker | SonarQube Docs 部署镜像 2.1 docker部署 # 宿主机执行 $. vi /etc/sysctl.conf…

网络安全——SQL注入实验

一、实验目的要求&#xff1a; 二、实验设备与环境&#xff1a; 三、实验原理&#xff1a; 四、实验步骤&#xff1a; 五、实验现象、结果记录及整理&#xff1a; 六、分析讨论与思考题解答&#xff1a; 七、实验截图&#xff1a; 一、实验目的要求&#xff1a; 1、…

言简意赅的 el-table 跨页多选

步骤一 在<el-table>中:row-key"getRowKeys"和selection-change"handleSelectionChange" 在<el-table-column>中type"selection"那列&#xff0c;添加:reserve-selection"true" <el-table:data"tableData"r…

嵌入式开发板qt gdb调试

1&#xff09; 启动 gdbserver ssh 或者 telnet 登陆扬创平板 192.168.0.253&#xff0c; 进入命令行执行如下&#xff1a; chmod 777 /home/HelloWorld &#xff08;2&#xff09; 打 开 QTcreator->Debug->StartDebugging->Attach to Running Debug Server 进行…

音乐制作软件Ableton Live 11 mac功能特点

Ableton Live 11 mac是一款数字音频工作站软件&#xff0c;用于音乐制作、录音、混音和现场演出是一款流行的音乐制作软件。 Ableton Live 11 mac特点和功能 Comping功能&#xff1a;Live 11增加了Comping功能&#xff0c;允许用户在不同的录音轨道上进行多次录音&#xff0c;…

西南科技大学数字电子技术实验四(基本触发器逻辑功能测试及FPGA的实现)预习报告

一、计算/设计过程 说明:本实验是验证性实验,计算预测验证结果。是设计性实验一定要从系统指标计算出元件参数过程,越详细越好。用公式输入法完成相关公式内容,不得贴手写图片。(注意:从抽象公式直接得出结果,不得分,页数可根据内容调整) (1)D触发器 特征方程: Q…

网络层--TCP/UDP协议

目录 一、TCP/UDP协议介绍 1、UDP(User Datagram Protocol)--用户数据报协议 1.1 UDP报文格式 1.2 UDP协议的特性 2、TCP(Transmission Control Protocol )--传输控制协议 2.1 TCP报文格式 2.2 TCP协议的特性 2.3 TCP三次握手 2.4 四次挥手 三、TCP和UDP的区别 四、t…

技术分享 | app测试中常用的Android模拟器

Emulator Emualor 是 Android Studio 自带的模拟器&#xff0c;是官方提供的工具&#xff0c;Android 开发最常使用的就是这一款。 它功能非常齐全&#xff0c;电话本、通话等功能都可正常使用。用户可以使用键盘输入&#xff0c;鼠标点击模拟器按键输入&#xff0c;甚至还可以…

Spring基于xml半注解开发

目录 Component的使用 依赖注解的使用 非自定义Bean的注解开发 Component的使用 基本Bean注解&#xff0c;主要是使用注解的方式替代原有的xml的<bean>标签及其标签属性的配置&#xff0c;使用Component注解替代<bean>标签中的id以及class属性&#xff0c;而对…

【Android逆向】记录一次某某虚拟机的逆向

导语 学了一段时间的XPosed&#xff0c;发现XPosed真的好强&#xff0c;只要技术强&#xff0c;什么操作都能实现... 这次主要记录一下我对这款应用的逆向思路 apk检查 使用MT管理器检查apk的加壳情况 发现是某数字的免费版本 直接使用frida-dexdump 脱下来后备用 应用分…

yolov8实战第二天——yolov8训练结果分析(保姆式解读)

yolov8实战第一天——yolov8部署并训练自己的数据集&#xff08;保姆式教程&#xff09;-CSDN博客 我们在上一篇文章训练了一个老鼠的yolov8检测模型&#xff0c;训练结果如下图&#xff0c;接下来我们就详细解析下面几张图。 一、混淆矩阵 正确挑选&#xff08;正确&#…

gRPC框架

1、gRPC 与 Protobuf 介绍 微服务架构中&#xff0c;由于每个服务对应的代码库是独立运行的&#xff0c;无法直接调用&#xff0c;彼此间 的通信就是个大问题gRPC 可以实现微服务&#xff0c; 将大的项目拆分为多个小且独立的业务模块&#xff0c; 也就是服务&#xff0c; 各服…

如何做一名“机关算尽”的伦敦银投资者?

做伦敦银投资者有一个好处&#xff0c;就是这个世界上投资者均可以参与的市场会比较公平&#xff0c;所以我们从书中、从课程中学习到的分析方法&#xff0c;可以应用在该市场中。其中有一种方法&#xff0c;可以通过计算&#xff0c;算出银价转折、反转的点位等等。那我们如何…

拆解大语言模型 RLHF 中的PPO算法

为什么大多数介绍大语言模型 RLHF 的文章&#xff0c;一讲到 PPO 算法的细节就戛然而止了呢&#xff1f;要么直接略过&#xff0c;要么就只扔出一个 PPO 的链接。然而 LLM x PPO 跟传统的 PPO 还是有些不同的呀。 其实在 ChatGPT 推出后的相当一段时间内&#xff0c;我一直在等…

一些AG10K FPGA 调试的建议-Douglas

PLL AGM FPGA 在配置成功时&#xff0c;PLL 已经完成锁定&#xff0c;lock 信号已经变高&#xff1b;如果原设计中用 lock 信号输出实现系统 reset 的复位功能&#xff0c;就不能正确完成上电复位&#xff1b;同时&#xff0c;为了保证 PLL 相移的稳定&#xff0c;我们需要在 P…

RabbitMq的详细使用

消息队列RabbitMQ详细使用 文章目录 消息队列RabbitMQ详细使用MQ 的相关概念什么是MQ为什么要用MQMQ 的分类MQ 的选择 RabbitMQRabbitMQ 的概念四大核心概念各个名词介绍安装RabbitMQWeb管理界面及授权操作Docker 安装Hello world简单示例 Work Queues轮训分发消息消息应答自动…

Java编程中通用的正则表达式(一)

正则表达式&#xff08;Regular Expression&#xff0c;简称RegEx&#xff09;&#xff0c;又称常规表示法、正则表示、正规表示式、规则表达式、常式、表达式等&#xff0c;是计算机科学中的一个概念。正则表达式是用于描述某种特定模式的字符序列&#xff0c;特别是用来匹配、…

设计模式(二)-创建者模式(5)-建造者模式

一、为何需要建造者模式&#xff08;Builder&#xff09;? 在软件系统中&#xff0c;会存在一个复杂的对象&#xff0c;复杂在于该对象包含了很多不同的功能模块。该对象里的各个部分都是按照一定的算法组合起来的。 为了要使得复杂对象里的各个部分的独立性&#xff0c;以及…

大模型学习之GPT系列

GPT系列 预备知识GPT-1无监督预训练有监督的微调训练 GPT-2数据集&#xff1a;输入表示模型实验 GPT-3模型数据集实验局限性 InstructGPTGPT-4GPT-4 新特性基础能力 参考文献 大模型 GPT演进路线图 预备知识 Transformer 结构图 GPT-1 首先使用未标注的数据训练一个预训练…

速学数据结构 | 树 森林 二叉树 的概念详讲篇

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《速学数据结构》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! &#x1f4cb; 前言 &#x1f308;hello&#xff01; 各位宝子们大家好啊&#xff0c;关于线性表我们已经在前面更新完了…