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

news2024/11/17 21:44:11

目录

一.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赋初始值200
	if (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.coloradmin.cn/o/1314099.html

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

相关文章

innerHTML、innerText、textContent有什么区别

innerHTML、innerText、textContent有什么区别 在 HTML 中&#xff0c;innerHTML、innerText、 和textContent是 DOM&#xff08;文档对象模型&#xff09;的属性。它们允许我们读取和更新 HTML 元素的内容。 但它们在包含的内容以及处理 HTML 标签的方式有不同的行为。 读完…

安恒明御安全网关 aaa_local_web_preview文件上传漏洞复现

0x01 产品简介 明御安全网关秉持安全可视、简单有效的理念,以资产为视角,构建全流程防御的下一代安全防护体系,并融合传统防火墙、入侵检测、入侵防御系统、防病毒网关、上网行为管控、VPN网关、威胁情报等安全模块于一体的智慧化安全网关。 0x02 漏洞概述 明御安全网关在…

jmeter,断言:响应断言、Json断言

一、响应断言 接口A请求正常返回值如下&#xff1a; {"status": 10013, "message": "user sign timeout"} 在该接口下创建【响应断言】元件&#xff0c;配置如下&#xff1a; 若断言成功&#xff0c;则查看结果树的接口显示绿色&#xff0c;若…

Spring Boot启动慢如何分析

如果发现项目启动慢&#xff0c;你知道怎么分析慢的原因吗&#xff1f; 分析方法 自定义监听器 SpringApplicationRunListener是Spring Boot中的一个接口&#xff0c;它的作用是在SpringApplication运行的各个阶段提供回调接口&#xff0c;以便我们可以在这些阶段执行自定义…

PPT插件-好用的插件-放映笔、绘图板-大珩助手

放映笔 幻灯片放映时&#xff0c;工具在幻灯片的左下方&#xff0c;本工具在幻灯片的右侧&#xff0c;可以移动&#xff0c;可以方便在右侧讲课时候使用 绘图板 可在绘图板上写签名、绘制图画、写字等等&#xff0c;点画笔切换橡皮擦&#xff0c;点插入绘图&#xff0c;将背景…

jenkins学习19 - pipline 构建项目生成 allure报告并发送邮箱

前言 个人其实一直的不太喜欢用邮箱发送报告&#xff0c;测试报告用邮件通知这都是五六年前的事情了&#xff0c;但有部分小伙伴依然执着于发邮件报告通知。 这里整理了下发邮箱通知的教程。 配置你的邮箱 配置邮箱这一步最繁琐&#xff0c;由于每个人使用的邮箱不一样&…

欧拉市场份额达36.8%,服务器操作系统市场份额第一

[中国&#xff0c;北京&#xff0c;2023年12月15日] 以“崛起数字时代&#xff0c;引领数智未来”为主题的操作系统大会2023今日在北京国家会议中心举办&#xff0c;大会由开放原子开源基金会、中国电子技术标准化研究院、国家工业信息安全发展研究中心、中国软件行业协会共同主…

【Java】SpringBoot中实现Redis Stream队列

SpringBoot实现Redis Stream队列 前言 简单实现一下在SpringBoot中操作Redis Stream队列的方式&#xff0c;监听队列中的消息进行消费。 jdk&#xff1a;1.8 springboot-version&#xff1a;2.6.3 redis&#xff1a;5.0.1&#xff08;5版本以上才有Stream队列&#xff09;…

机器学习算法---回归

1. 线性回归&#xff08;Linear Regression&#xff09; 原理&#xff1a; 通过拟合一个线性方程来预测连续响应变量。线性回归假设特征和响应变量之间存在线性关系&#xff0c;并通过最小化误差的平方和来优化模型。优点&#xff1a; 简单、直观&#xff0c;易于理解和实现。…

力扣题:数字与字符串间转换-12.16

力扣题-12.16 [力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 力扣题1&#xff1a;640. 求解方程 解题思想&#xff1a;首先将方程按照“”进行划分&#xff0c;然后分别记录x的因数和常数项&#xff0c;最后进行返回的判断即可 class Solution(object):def solveEqu…

娱乐新拐点:TikTok如何改变我们的日常生活?

在数字时代的浪潮中&#xff0c;社交媒体平台不断涌现&#xff0c;其中TikTok以其独特的短视频内容在全球范围内掀起了一场娱乐革命。本文将深入探讨TikTok如何改变我们的日常生活&#xff0c;从社交互动、文化传播到个人创意表达&#xff0c;逐步改写了娱乐的新篇章。 短视频潮…

【Idea】SpringBoot项目中,jar包引用冲突异常的排查 / SM2算法中使用bcprov-jdk15to18的报错冲突问题

问题描述以及解决方法&#xff1a; 项目中使用了bcprov-jdk15to18 pom依赖&#xff0c;但是发现代码中引入的版本不正确。 追溯代码发现版本引入的是bcprov-jdk15on&#xff0c;而不是bcprov-jdk15to18&#xff0c;但是我找了半天pom依赖也没有发现有引入bcprov-jdk15on依赖。…

【MySQL】:表的增加和查寻

表的增查 一.Create(增)1.基本插入2.插入是否更新3.替换 二.Retrieve(查)1.select列1.全列查询2.指定列查询3.查询字段为表达式4.结果去重 2.where条件查询1.运算符2.运算符使用 3.结果排序4.筛选分页结果 一.Create(增) 1.基本插入 对于表的增加&#xff0c;前面已经用过很多…

什么是供应链安全及其工作原理?

6000公里长的丝绸之路将丝绸、谷物和其他货物从中国运送到帕尔米拉。尽管蒙古治下的和平保护丝绸之路免受海盗、强盗和内部盗窃的侵害&#xff0c;但商人仍然装备精良&#xff0c;并依赖于大型商队旅行和战略性放置的小型堡垒所提供的安全。 为什么供应链安全很重要&#xff1…

(1)(1.8) MSP(MultiWii 串行协议)(4.1 版)

文章目录 前言 1 协议概述 2 配置 3 参数说明 前言 ArduPilot 支持 MSP 协议&#xff0c;可通过任何串行端口进行遥测和传感器。这允许 ArduPilot 将其遥测数据发送到 MSP 兼容设备&#xff08;如大疆护目镜&#xff09;&#xff0c;用于屏幕显示&#xff08;OSD&#xff…

微服务保护--熔断降级

1.熔断降级介绍 熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例&#xff0c;如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求&#xff1b;而当服务恢复时&#xff0c;断路器会放行访问该服务的请求。 断路器控制熔断和放行…

ISCTF(a)

where_is_the_flag 答案应该被分成了三份了 蚁剑连接看看 第一个 第二个 第三个&#xff0c;在www下 Yunxi{0797d78c-0cb2-4cfb-87e6-f9c102f716f3} 命令执行 POST : 1 system ( tac flag.php ); 1 system ( tac /flag2 ); 1 system ( env ); 1z_Ssql 使用万能密码 后…

【LeetCode刷题笔记(7-1)】【Python】【四数之和】【哈希表】【中等】

文章目录 四数之和题目描述示例 1示例 2提示解决方案1&#xff1a;【四层遍历查找】解决方案2&#xff1a;【哈希表】【三层遍历】 结束语 四数之和 四数之和 题目描述 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件…

鸿蒙原生应用/元服务开发-Stage模型能力接口(五)

说明 Common模块将二级模块API组织在一起方便开发者进行导出。本模块首批接口从API version 9开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。本模块接口仅可在Stage模型下使用 二、 导入模块 import common from ohos.app.ability.common; 三、 …

动手学深度学习-自然语言处理:应用

自然语言处理中的应用主要有哪些&#xff1f; 自然语言处理&#xff1a;应用 情感分析及数据集 情感分析研究人们在文本中的情感&#xff0c;这被认为是一个文本分类问题&#xff0c;它将可变长度的文本序列进行转换为固定长度的文本类别。经过预处理后&#xff0c;我们可以使…