【C语言__动态内存管理__复习篇6】

news2025/1/15 12:51:12

目录

前言

一、动态内存管理

二、动态内存函数

        2.1 malloc 

        2.2 free

        2.3 calloc

        2.4 realloc

三、动态内存常见的6个使用错误

        3.1 接收malloc/calloc返回的参数后未及时检查是否为NULL

        3.2 越界访问动态内存空间

        3.3 对非动态开辟的内存使用free释放

        3.4 使用free只释放了动态内存的一部分

        3.5 对同一块动态内存多次释放

        3.6 忘记释放动态内存造成内存泄露

四、动态内存经典笔试题

        4.1 题目1

        4.2 题目2

        4.3 题目3

        4.4 题目4

五、柔性数组

六、C/C++中程序内存区域划分


前言

本篇主要讨论以下问题:

动态内存基础知识点:

1. 为什么会出现动态内存的概念

2. 与动态内存有关的4个函数它们分别的作用、函数的参数、返回类型以及要注意的地方是什么

3. 理解动态开辟的内存是在堆区开辟空间的,如果不主动使用free释放开辟的动态内存空间,那么只能等程序结束后由操作系统释放了。(不可取,一定要记得自己用free去释放)

动态内存避坑指南:

4. 动态内存开辟和使用过程中常见有哪6大错误

5. 动态内存经典笔试题,找出代码中错误的点

柔性数组:

6. 怎样的数组才能被称为柔性数组,含柔性数组的结构体如何开辟内存空间,如何找到结构体变量的成员变量的

了解内存有哪几个重要的区域:

7.内存有哪几个重要的区域

 一、动态内存管理

1. 在没有学习动态内存管理前 ,我们知道的为变量开辟内存的方式有两种:① 一次申请一个变量的空间 ② 一次申请一块连续的空间,但这两种申请内存空间的方式有一个明显的缺点,就是申请的空间大小一旦确定就无法更改了,于是C语言中引入了动态内存的概念,我们可以利用4个与动态内存有关的函数,实现动态开辟内存空间的效果。

二、动态内存函数

1. 4个与动态内存有关的函数所需的头文件都是<stdlib.h>

2.1 malloc 

1. malloc函数的作用:开辟size字节的动态内存空间。

2. 语法形式:void* malloc (size_t size)

    size_t size:表示向堆区一次性申请几个比特位的空间

    void*:返回类型之所以是void*(void*可接收任意类型的地址)是因为malloc只知道向内存的堆区申请几个字节的空间,并不知道将会存放什么类型的数据,但是程序员是知道的,所以在用malloc开辟动态内存后,一般会立即强制类型转换,直接用其他类型的指针变量接收

3. 如果malloc开辟内存成功,则返回一个指向开辟好空间的指针。

    如果malloc开辟内存失败,则返回一个NULL指针,因此在用指针接收malloc返回的地址后,需要检查指针内的地址是否为NULL

4. 如果malloc函数的参数size为0,这是标准未定义的,完全取决于编译器如何处理。

#include <stdio.h>
#include <stdlib.h>
int main()
{
	//开辟动态内存
	int* ps = (int*)malloc(10 * sizeof(int));//强制转换后再接收
	if (ps == NULL)//判断是否开辟失败
	{
		perror("malloc");
		return 1;//异常返回,返回非0
	}
	return 0;
}

2.2 free

1. free函数的作用:用于回收malloc/calloc/realloc开辟的动态内存空间

2. 语法形式:void free (void* ptr)

    void* ptr:表示指向malloc/calloc/realloc开辟的动态内存空间的指针。

3. free函数只是回收了ptr所指向的动态内存空间,但此时ptr中仍然存放着指向刚刚已被回收的动态内存空间的地址,因此在使用free函数后,应立即在后面一行中写上将ptr置NULL的操作,防止ptr野指针被使用。

4. 如果free函数的参数 ptr 指向的空间不是动态开辟的内存空间,这是标准未定义的,完全取决于编译器如何处理。

    如果free函数的参数是NULL,则free函数将什么都不会做。

#include <stdio.h>
#include <stdlib.h>
int main()
{
	//开辟动态内存
	int* ps = (int*)malloc(10 * sizeof(int));//强制转换后再接收
	if (ps == NULL)//判断是否开辟失败
	{
		perror("malloc");
		return 1;//异常返回,返回非0
	}
	//使用动态内存

	//释放动态内存
	free(ps);
	ps = NULL;
	return 0;
}

2.3 calloc

1. calloc函数的作用:为num个大小为size字节的元素开辟一块动态内存空间, 与malloc不同的是calloc会对开辟的空间每个字节都初始化为0

2. 语法形式:void* calloc (size_t num, size_t size)

    size_t mun:表示要分配的元素个数。

    size_t size:表示每个元素的大小,单位是比特位

    void*:和malloc函数的原因一致,在用calloc开辟动态内存后,一般会立即强制类型转换,直接用其他类型的指针变量接收

3. 如果calloc开辟内存成功,则返回一个指向开辟好空间的指针。

    如果calloc开辟内存失败,则返回一个NULL指针,因此在用指针接收malloc返回的地址后,需要检查指针内的地址是否为NULL

#include <stdio.h>
#include <stdlib.h>
int main()
{
	//开辟动态内存
	int* ps = (int*)calloc(10, sizeof(int));//强制转换后再接收
	if (ps == NULL)//判断是否开辟失败
	{
		perror("calloc");
		return 1;//异常返回,返回非0
	}
	//使用动态内存,看看calloc默认初始化的值
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(ps + i));//屏幕上打印出了10个0
	}
	//释放动态内存
	free(ps);
	ps = NULL;
	return 0;
}

2.4 realloc

1. realloc函数的作用:调整用malloc/calloc/realloc开辟过的动态内存空间大小

2. 语法形式:void* realloc (void* ptr, size_t size)

    void* ptr:表示指向malloc/calloc/realloc开辟的动态内存空间的指针。

    size_t size:表示想要重新调整到的动态内存空间的大小,单位是比特位

    void*:和malloc函数的原因一致,在用calloc开辟动态内存后,一般会立即强制类型转换,直接用其他类型的指针变量接收

3. 如果realloc调整内存大小成功,则返回非NULL指针。

    如果realloc调整内存大小失败,则返回一个NULL指针,此时如果直接用ptr接收realloc返回的地址,会使得ptr连原本的动态内存空间都无法找到,也无法将原本的动态内存空间释放,所以在用realloc调整动态内存空间后,不会直接用ptr接收,而是会建立一个临时指针去接收,临时的指针接收后,判断如果临时指针不为NULL,则把临时指针内的地址赋值给ptr,并把临时的指针赋值为NULL;如果为NULL,则进行报错

4. 关于realloc函数返回的地址是什么的问题:

    情况一:原有空间之后有⾜够⼤的空间可以扩充,此时要扩展内存就直接会在原有内存之后直接追加空间,原来空间的数据不发⽣变化,realloc返回的地址值与ptr内的值一致。

    情况二:原有空间之后没有⾜够⼤的空间可以扩充,扩展的⽅法是,在堆空间上另找⼀个合适⼤⼩的连续空间来使⽤,同时把旧数据拷贝到新的空间,并释放旧的空间,这样函数返回的是⼀个新的内存地址。

5. realloc函数的隐藏技能:如果realloc函数的参数void* ptr部分传参为NULL,效果等同于malloc函数。

#include <stdio.h>
#include <stdlib.h>
int main()
{
	//开辟动态空间
	int* ps = (int*)malloc(10 * sizeof(int));
	if (ps == NULL)
	{
		perror("malloc");
		return 1;
	}
	//调整动态空间
	int* ps2 = (int*)realloc(ps, 20 * sizeof(int));
	if (ps2 != NULL)
	{
		ps = ps2;
		ps2 = NULL;
	}
	else
	{
		perror("realloc");
		return 1;
	}
	//释放动态内存
	free(ps);
	ps = NULL;
	return 0;
}

三、动态内存常见的6个使用错误

3.1 接收malloc/calloc返回的参数后未及时检查是否为NULL

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	*p = 20;//err
	return 0;
}

3.2 越界访问动态内存空间

#include <stdio.h>
#include <stdlib.h>
int main()
{
	//开辟动态内存空间
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//使用
	for (int i = 0; i < 12; i++)
	{
		*(p + i) = i;//err
	}
	//释放动态内存空间
	free(p);
	p = NULL;
	return 0;
}

3.3 对非动态开辟的内存使用free释放

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int a = 10;
	int* pa = &a;
	free(pa);//err
	pa = NULL;
	return 0;
}

3.4 使用free只释放了动态内存的一部分

#include <stdio.h>
#include <stdlib.h>
int main()
{
	//开辟动态内存空间
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//使用动态空间
	for (int i = 0; i < 5; i++)
	{
		*p = i;
		p++;
	}
	//释放动态内存空间
	free(p);//err
	p = NULL;
	return 0;
}

3.5 对同一块动态内存多次释放

#include <stdio.h>
#include <stdlib.h>
int main()
{
	//开辟动态内存空间
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//使用动态空间
	free(p);
	p = NULL;
	//释放动态内存空间
	free(p);//err
	p = NULL;
	return 0;
}

3.6 忘记释放动态内存造成内存泄露

#include <stdio.h>
#include <stdlib.h>

void Test()
{
	int* p = (int*)malloc(100);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	else
	{
		*p = 20;
	}
	//err,未释放动态内存
}

int main()
{
	test();
	return 0;
}

四、动态内存经典笔试题

4.1 题目1

题目分析:代码的目的是想让字符串拷贝到动态开辟的内存中。

错误分析:① 采用了传值调用,改变形参,实参不受影响。

                  ② NULL不能被使用。

                  ③ 没有free动态内存。

#include <stdio.h>
#include <stdlib.h>

void GetMemory(char* p)
{
	p = (char*)malloc(100);
}

void Test(void)
{
	char* str = NULL;
	GetMemory(str);//传值调用
	strcpy(str, "hello world");//str==NULL,err
	printf(str);
}

int main()
{
	Test();
	return 0;
}
//修改代码
#include <stdio.h>
#include <stdlib.h>

void GetMemory(char** p)
{
	*p = (char*)malloc(100);
    if (*p == NULL)//
    {
        perror("malloc");
    }
}

void Test(void)
{
	char* str = NULL;
	GetMemory(&str);//
	strcpy(str, "hello world");
	printf(str);
	free(str);//
	str = NULL;//
}

int main()
{
	Test();
	return 0;
}

4.2 题目2

错误分析:① 自定义函数返回了局部变量的地址

#include <stdio.h>
#include <stdlib.h>

char* GetMemory(void)
{
	char p[] = "hello world";//err
	//static char p[] = "hello world";//修改代码:将上一行替换
	return p;
}

void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}

int main()
{
	Test();
	return 0;
}

4.3 题目3

错误分析:① 没有判断malloc是否返回的NULL

                  ② 没有用free释放动态内存空间

#include <stdio.h>
#include <stdlib.h>

void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}

void Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
}

int main()
{
	Test();
	return 0;
}
//修改代码
#include <stdio.h>
#include <stdlib.h>

void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
	if (*p == NULL)//
	{
		perror("malloc");
	}
}

void Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);//
	free(str);//
	str = NULL;
}

int main()
{
	Test();
	return 0;
}

4.4 题目4

错误分析:① 没有判断malloc是否返回的NULL

                  ② free后没有及时将str置NULL

#include <stdio.h>
#include <stdlib.h>

void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf("str");
	}
}

int main()
{
	Test();
	return 0;
}
//修改代码
#include <stdio.h>
#include <stdlib.h>

void Test(void)
{
	char* str = (char*)malloc(100);
	if (str == NULL)
	{
		perror("malloc");
		return 1;
	}
	strcpy(str, "hello");
	free(str);
	str = NULL;
	//无意义代码
	if (str != NULL)
	{
		strcpy(str, "world");
		printf("str");
	}
}

int main()
{
	Test();
	return 0;
}

五、柔性数组

1. C99中,结构体至少2个成员变量,若最后一个成员变量是一个未知大小的数组,则这个数组叫做柔性数组。( 未知大小的数组写法:① int arr[]; ② int arr[0];

2. 柔性数组的特点:

① 结构体中柔性数组成员前必须至少有1个其他成员变量。

② sizeof计算含柔性数组的结构体大小时,计算的结果不包含柔性数组的大小,因为柔性数组的大小是未知的。

③ 包含柔性数组的结构体在申请内存空间时要采用动态内存开辟的方式,并且开辟的动态内存空间应大于结构体内存的大小,以适应柔性数组的预期大小(总之,在创建有柔性数组的结构体变量时,不要采用struct St s;传统的方式,这样创建的结构体变量中柔性数组成员变量是没有大小的,我们应采用

struct St* p = (struct St*) malloc(sizeof(struct St)+10* sizeof(int);类似的方式创建结构体变量,找结构体成员变量时直接用(->)操作符即可,这种写法下相当于不同的结构体指针变量,代表着不同的结构体变量)

3. 使用动态内存为含柔性数组的结构体开辟空间的方式,而不用平常为结构体变量开辟空间的方式的好处:① 方便内存释放 ② 访问数组相对较快 ③ 利于减少内存碎片(动态内存之间未利用到的内存称为内存碎片)

#include <stdio.h>
#include <stdlib.h>

struct St
{
	float a;
	int arr[0];
};

int main()
{
	struct St* p = (struct St*)malloc(sizeof(struct St) + 10 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//使用
	scanf("%f", &(p->a));
	printf("%f", p->a);
	for (int i = 0; i < 10; i++)
	{
		scanf("%d", &(p->arr[i]));
		printf("%d ", p->arr[i]);
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}

 六、C/C++中程序内存区域划分

 

本篇文章已完结,谢谢支持!!! 

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

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

相关文章

STM32学习和实践笔记(17):STM32外部中断(EXTI)的整体介绍

1.外部中断介绍 1.1 EXTI简介 STM32F10x外部中断/事件控制器&#xff08;EXTI&#xff09;包含多达 20 个用于产生事件/中断请求的边沿检测器。&#xff08;事件与中断的区别&#xff0c;可参看STM32---中断与事件的区别_中断和事件的区别-CSDN博客&#xff09; 具体有哪些&a…

蓝桥杯2024年第十五届省赛真题-爬山

贪心优先队列的题&#xff0c;贪心会漏一个情况&#xff0c;不知道怎么处理&#xff0c;这里直接打表了 2 1 1 48 49 答案是30&#xff0c;贪心是31 专有名词&#xff1a;hack-有新的测试点过不了 #include<bits/stdc.h> using namespace std; #define endl \n #define …

AI容器化部署开发尝试 (一)(Pycharm连接docker,并部署django测试)

注意&#xff1a;从 Docker 19.03 开始&#xff0c;Docker 引入了对 NVIDIA GPU 的原生支持&#xff0c;因此若AI要调用GPU算力的话docker版本也是有要求的&#xff0c;后面博客测试。 当然本篇博客还没设计到GPU的调用&#xff0c;主要Pycharm加Anaconda的方案用习惯了&#…

【配电网故障定位】基于二进制矮猫鼬优化算法的配电网故障定位 33节点配电系统故障定位【Matlab代码#82】

文章目录 【获取资源请见文章第6节&#xff1a;资源获取】1. 配电网故障定位2. 二进制矮猫鼬优化算法3. 算例展示4. 部分代码展示5. 仿真结果展示6. 资源获取 【获取资源请见文章第6节&#xff1a;资源获取】 1. 配电网故障定位 配电系统故障定位&#xff0c;即在配电网络发生…

JavaScript算数运算符

源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head> <b…

mars3d实现禁止地图移动,禁止地图左右平移,但是鼠标可以移动的效果。

new mars3d.layer.GeoJsonLayer({渲染后实现鼠标左键按住不释放拖动时&#xff0c;地图不跟着拖动效果 当前问题&#xff1a; 1.在map初始化&#xff0c;或者是加载效果的时候&#xff0c;整个地球的场景都是一样的。 如果鼠标左键按住不释放&#xff0c;在屏幕上拖动的时候…

软考136-上午题-【软件工程】-风险管理

一、风险管理 般认为软件风险包含两个特性&#xff1a;不确定性、损失。不确定性是指风险可能发生也可能不发生&#xff1b;损失是指如果风险发生&#xff0c;就会产生恶性后果。 在进行风险分析时&#xff0c;重要的是量化每个风险的不确定程度和损失程度。为了实现这一点&a…

Ceph学习 -11.块存储RBD接口

文章目录 RBD接口1.基础知识1.1 基础知识1.2 简单实践1.3 小结 2.镜像管理2.1 基础知识2.2 简单实践2.3 小结 3.镜像实践3.1 基础知识3.2 简单实践3.3 小结 4.容量管理4.1 基础知识4.2 简单实践4.3 小结 5.快照管理5.1 基础知识5.2 简单实践5.3 小结 6.快照分层6.1 基础知识6.2…

基于SSM的平面设计课程在线学习平台系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的平面设计课程在线学习平台系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;…

步步精科技获得发明型专利,提升Type-C连接器行业竞争力

在电子科技日新月异的时代&#xff0c;连接器作为电子设备中不可或缺的一部分&#xff0c;其安全性、稳定性和性能水平直接关系到设备的使用效果和用户体验。深圳市步步精科技有限公司&#xff08;以下简称“步步精科技”&#xff09;一直致力于连接器领域的技术创新和产品研发…

【论文阅读】用于遥感弱监督语义分割的对比标记和标签激活

【论文阅读】用于遥感弱监督语义分割的对比标记和标签激活 文章目录 【论文阅读】用于遥感弱监督语义分割的对比标记和标签激活一、介绍二、联系工作三、方法3.1 对比token学习模块&#xff08;CTLM&#xff09;3.2 Class token对比学习3.3 标签前景激活模块 四、实验结果 Cont…

【论文笔记 | 异步联邦】Asynchronous Federated Optimization

论文信息 Asynchronous Federated Optimization&#xff0c;OPT2020: 12th Annual Workshop on Optimization for Machine Learning&#xff0c;不属于ccfa introduction 背景&#xff1a;联邦学习有三个关键性质 任务激活不频繁&#xff08;比较难以达成条件&#xff09;&…

HarmonyOS开发案例:【首选项】

介绍 本篇Codelab是基于HarmonyOS的首选项能力实现的一个简单示例。实现如下功能&#xff1a; 创建首选项数据文件。将用户输入的水果名称和数量&#xff0c;写入到首选项数据库。读取首选项数据库中的数据。删除首选项数据文件。 最终效果图如下&#xff1a; 相关概念 [首…

网盘——私聊

在私聊这个功能实现中&#xff0c;具体步骤如下&#xff1a; 1、实现步骤&#xff1a; A、客户端A发送私聊信息请求&#xff08;发送的信息包括双方的用户名&#xff0c;聊天信息&#xff09; B、如果双方在线则直接转发给B&#xff0c;不在线则回复私聊失败&#xff0c;对方…

政安晨:【Keras机器学习示例演绎】(四)—— 利用迁移学习进行关键点检测

目录 数据收集 导入 定义超参数 加载数据 可视化数据 准备数据生成器 定义增强变换 创建训练和验证分割 数据生成器调查 模型构建 模型编译和训练 进行预测并将其可视化 更进一步 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏…

遍历取后端数据推送到地图上,实现图标点标记地图效果

遍历取后端数据推送到地图上&#xff0c;实现图标点标记地图效果 示例链接&#xff1a; 功能示例(Vue版) | Mars3D三维可视化平台 | 火星科技 踩坑注意点&#xff1a; 1. id: 1 是地图底图的id 后台也返回之后 id直接会有冲突 此时图标标记之后无法单击 相关代码&#xff1a…

liqo学习及安装,k8s,kubernetes多集群互联

先按照官方的教程在虚拟机安装学习 在开始以下教程之前&#xff0c;您应该确保您的系统上安装了以下软件&#xff1a; Docker&#xff0c;容器运行时。Kubectl&#xff0c;Kubernetes 的命令行工具。 curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.…

Spark-Scala语言实战(17)

我带着大家一起来到Linux集群环境下&#xff0c;学习我们的spark。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 Spark-Scala语言实战&#xff08;16&#x…

关于MCU核心板的一些常见问题

BGA植球与焊接&#xff08;多涂焊油&#xff09;&#xff1a; 【BGA芯片是真麻烦&#xff0c;主要是植锡珠太麻烦了&#xff0c;拆一次就得重新植】https://www.bilibili.com/video/BV1vW4y1w7oNvd_source3cc3c07b09206097d0d8b0aefdf07958 / NC电容一般有两种含义&#xff1…

js自动缩放页面,html自动缩放页面,大屏自动缩放页面,数字看板自动缩放页面,大数据看板自动缩放页面

js自动缩放页面&#xff0c;html自动缩放页面&#xff0c;大屏自动缩放页面&#xff0c;数字看板自动缩放页面&#xff0c;大数据看板自动缩放页面 由纯JS实现 html代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"…