【C语言__动态内存管理__复习篇6】
目录
前言
一、动态内存管理
二、动态内存函数
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;//errreturn 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);//errpa = 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);//errp = 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);//errp = 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,errprintf(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++中程序内存区域划分
本篇文章已完结,谢谢支持!!!
相关文章:

【C语言__动态内存管理__复习篇6】
目录 前言 一、动态内存管理 二、动态内存函数 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只释放了…...

C语言 | Leetcode C语言题解之第30题串联所有单词的子串
题目: 题解: typedef struct {char key[32];int val;UT_hash_handle hh; } HashItem;int* findSubstring(char * s, char ** words, int wordsSize, int* returnSize){ int m wordsSize, n strlen(words[0]), ls strlen(s);int *res (int *)mall…...

大数据dolphinscheduler 本地容器化安装
Minio 容器安装 docker run -p 9000:9000 -p 9090:9090 --name minio -d -e "MINIO_ACCESS_KEYminioadmin" -e "MINIO_SECRET_KEYminioadmin" -v D:\SF\DOCKER\minio\data:/data -v D:\SF\DOCKER\minio\config:/root/.minio minio/minio server /da…...

简单的车牌号识别
目录 处理流程与界面各接口编写时遇到的一些问题上传图片识别结果标签显示中文 处理流程与界面 首先点击“上传图片”按钮,可以选择文件夹中含有汽车车牌的图片,并显示在“图片框”中。 点击“检测车牌”按钮,会先对“图片框”中即含有汽车车…...

openGauss学习笔记-261 openGauss性能调优-使用Plan Hint进行调优-将部分Error降级为Warning的Hint
文章目录 openGauss学习笔记-261 openGauss性能调优-使用Plan Hint进行调优-将部分Error降级为Warning的Hint261.1 功能描述261.2 语法格式261.3 示例261.3.1 忽略非空约束261.3.2 忽略唯一约束261.3.3 忽略分区表无法匹配到合法分区261.3.4 更新/插入值向目标列类型转换失败 o…...

CSS水波纹效果
效果图: 1.创建一个div <div class"point1" click"handlePoint(1)"></div> 2.设置样式 .point1{width: 1rem;height: 1rem;background: #2ce92f;position: absolute;border-radius: 50%;z-index: 999;cursor: pointer;} 3.设置伪…...
迭代器模式:优雅地遍历数据集合
在软件设计中,迭代器模式是一种常见且有用的设计模式,它允许顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。这种模式在需要对集合进行遍历操作而又不想暴露集合内部结构的场景下非常有用。 一、迭代器模式的使用条件 访问集…...

c++总结笔记(一)
计算机可以将程序转化为二进制指令(即机器码),并由CPU执行,CPU会按照指令的顺序依次执行每个指令。 C语言特点: 简洁高效可移植模块化标准化 C语言的标准 C89(C90)标准C99标准C11标准 导入 使用include导入包含…...

[python][gradio]chatbot控件用法
chatbot模块是Gradio中的一个组件,用于展示聊天机器人的输出,包括用户提交的消息和机器人的回复。它支持一些Markdown语法,包括粗体、斜体、代码和图片等。Chatbot模块的输入不接受用户输入,而是通过函数返回的列表来设置聊天内容…...

Sublime Text下载,安装,安装插件管理器,下载汉化插件
SublimeTest官网 © Sublime Text中文网 下载安装 一路点击安装即可 安装插件管理器 管理器官网安装 - 包控制 (packagecontrol.io) 手动安装将3 位置点击网址下载 再打开SublimeTest 点击 选择第一个Browse Packages..... 将会跳转到文件夹中 进入上一个文件夹 在进入…...

c++ ,stl经常出现的<>尖括号其实就是模板类的实例化
通过比如vector<int> 实际上是调用了类似模板template<T t>class vector{...}实例化了一个使用int的vector类来进行定义,我们可以尝试简单的做一个自己的array类 template<typename T1 ,int d2> class array1 {private:T1 *p;int size;public:ar…...

goproxy 简单介绍 及一键安装脚本
goproxy 官网 https://goproxy.cn/ GoProxy 是一项用于 Go 模块的高性能代理服务,旨在为 Go 开发人员提供更快速、更可靠的模块下载体验。它提供以下主要功能: 全球分布式代理服务器: GoProxy 在全球多个地区部署了代理服务器,例如拉斯维加…...

Day13-Python基础学习之数据分析案例
数据分析案例 data_define.py # 数据定义的类 class Record:def __init__(self, date, order_id, money, province):self.date dateself.order_id order_idself.money moneyself.province province def __str__(self):return f"{self.date}, {self.order_id}, {se…...

研究生,该学单片机还是plc。?
PLC门槛相对较低,但是在深入学习和应用时,仍然有很高的技术要求。我这里有一套单片机入门教程,不仅包含了详细的视频 讲解,项目实战。如果你渴望学习单片机,不妨点个关注,给个评论222,私信22&am…...
【Java】导出Mysql表表结构与注释数据字典
需求: 把mysql中所有表的字段名、数据类型、长度、注释整理成csv,做成数据字典。 import java.io.IOException; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.ResultSet; import ja…...

第07-2章 TCP/IP模型
7.7 TCP/IP模型详解 7.7.1 简介 应用层的PDU>APDU(Application PDU) 表示层的PDU>PPDU(Presentation PDU) 会话层的PDU>SPDU(Session PDU) 7.7.2 TCP/IP协议体系 (1)TCP…...

【办公类-21-15】 20240410三级育婴师 712道单选题(题目与答案合并word)
作品展示 背景需求: 前文将APP题库里的育婴师题目下载到EXCEL,并进行手动整理 【办公类-21-13】 2024045三级育婴师 721道单选题 UIBOT下载整理-CSDN博客文章浏览阅读451次,点赞10次,收藏3次。【办公类-21-13】 2024045三级育婴…...
Vue3+Vant开发:登录功能
🙈作者简介:练习时长两年半的Java up主 🙉个人主页:程序员老茶 🙊 ps:点赞👍是免费的,却可以让写博客的作者开心好久好久😎 📚系列专栏:Java全栈,…...

Linux程序调试优化(1)——内存占用详解及优化思路
文章目录 1.free查看总体的内存占用2./proc/$PID/status 查看某进程状态 linux开发最重要的两个参数,分别是内存以及CPU使用率,若内存出现严重不足,则在需要使用内存时,可能出现申请不到的情况,导致 OOM,L…...

高效解决Visual Studio Code中文乱码问题
文章目录 问题解决步骤 问题 Visual Studio Code新建一个文件编码方式总是默认GBK,如果我不修改成默认UTF-8,那么每次运行,如果有中文需要输出就会乱码! 解决步骤 之后我会持续更新,如果喜欢我的文章,请记…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...

华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...