C语言进阶---动态内存管理

动态内存管理

  • 前言:
  • 一、为什么存在动态内存分配?
  • 二、动态内存函数的介绍
    • 1.数据在不同区域的储存:
    • 2、malloc和free
    • 3、calloc
    • 4、realloc
  • 三、常见的动态内存错误
    • 1、对NULL指针的解引用操作
    • 2、对动态开辟空间的越界访问
    • 3、对非动态内存开辟的空间进行free
    • 4、对动态内存开辟的空间只进行一部分释放
    • 5、对同一块动态内存多次释放
    • 6、动态开辟内存忘记释放(内存泄漏)
  • 四、几个经典的笔试题
  • 五、柔性数组
    • 1.什么是柔性数组?
    • 2、柔性数组的特点
    • 3、柔性数组的使用
    • 4、柔性数组的优势

前言:

一、为什么存在动态内存分配?

常规的情况:

#define _CRT_SECURE_NO_WARNINGS 1 
#include <stdio.h>
int main()
{int val = 20;//在栈空间上开辟4个字节char arr[10] = { 0 };//在栈空间上开辟10个连续空间的字节return 0;
}

在这里插入图片描述

二、动态内存函数的介绍

1.数据在不同区域的储存:

在这里插入图片描述

2、malloc和free

malloc函数的介绍:
malloc是C语言中开辟指定空间字节大小的函数:
在这里插入图片描述
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针

  • 如果开辟成功,则返回一个指向开辟好空间的指针。
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
  • 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

free函数的介绍:
语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的,函数原型如下:
在这里插入图片描述
free函数用来释放动态开辟的内存:

  • 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
  • 如果参数 ptr 是NULL指针,则函数什么事都不做。
#include <stdio.h>
int main()
{//代码1int num = 0;scanf("%d", &num);int arr[num] = { 0 };//代码2int* ptr = NULL;ptr = (int*)malloc(num * sizeof(int));if (NULL != ptr)//判断ptr指针是否为空{int i = 0;for (i = 0; i < num; i++){*(ptr + i) = 0;}}free(ptr);//释放ptr所指向的动态内存ptr = NULL;//是否有必要?return 0;
}

3、calloc

calloc也是动态内存开辟的函数:

  • 它是开辟num个size大小的字节空间
  • 与malloc不同的是,他在返回开辟空间的起始地址之前,已经把开辟的那个空间全部都初始化为0
int main()
{	int* p = (int*)calloc(10, sizeof(int));if (NULL != p){//使用空间}free(p);p = NULL;return 0;
}

calloc函数与malloc函数的最大区别是:calloc函数会将开辟的那块空间全部都初始化为0。

4、realloc

  • realloc函数的出现会让动态内存管理更加灵活!
  • 有时我们发现申请的空间要么过大,要么过小,不能够很好地充分利用开辟的内存空间,所以说这个时候realloc的出现如同救世主一般。
    在这里插入图片描述
    参数1(void* ptr):指向先前分配有内存块空间的指针,也可以是空指针,但是NULL的话,这个realloc函数的功能就和malloc一样。
    参数2(size_t size):内存块的新大小,以字节为单位,size_t就是无符号整形。

对于realloc的返回值有三种情况:

  • 情况1. 如果需要在原空间上再开辟新的空间,而且原空间后方有足够大的空间,够开辟,那么就在原空间后面追加:
    在这里插入图片描述

  • 情况2:如果原空间后方的空间已经被占用,没有地方再开辟。那么realloc函数会在堆区中重新找一块能够储存这么大空间的地方,并且返回这个新空间的地址,然后把原来空间的内存释放(还给操作系统)。
    在这里插入图片描述

  • 情况三:开辟空间失败返回NULL
    具体代码进行分析:

int main()
{int* p = (int*)malloc(100);if (p == NULL)//开辟失败{perror("malloc fail");exit(-1);}else{//开辟成功}p = realloc(p, 1000);//这样可行吗?//nono!假设realloc开辟失败返回NULL,相当于我们现在直接将NULL赋给了p,//那么原来的空间已经消失不在了,出现了内存泄漏。//解决方法:int* ptr = realloc(p, 1000);//空瓶思想:我们先创建一个指针变量相当于空瓶,来暂时存放新开辟的空间地址//如果开辟成功,我们再把它赋给原来空间的起始地址p。//如果开辟失败,那么这样也避免了内存泄漏,还会找到原来那块空间的地址:pif (ptr!=NULL){p = ptr;}else{perror("realloc fail");}free(ptr);ptr = NULL;return 0;
}

三、常见的动态内存错误

1、对NULL指针的解引用操作

void test()
{int *p = (int *)malloc(INT_MAX/4);*p = 20;//如果p的值是NULL,就会有问题free(p);
}

不能对空指针进行free!!!

2、对动态开辟空间的越界访问

void test()
{int i = 0;int *p = (int *)malloc(10*sizeof(int));if(NULL == p){exit(EXIT_FAILURE);}for(i=0; i<=10; i++){*(p+i) = i;//当i是10的时候越界访问}free(p);
}

3、对非动态内存开辟的空间进行free

void test()
{int a = 10;int* p = &a;free(p);//不能对在栈区上开辟的内存进行free!!!
}

4、对动态内存开辟的空间只进行一部分释放

void test()
{int* p = (int*)malloc(100);p++;free(p);// P不在指向动态内存的起始位置
}

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

void test()
{int *p = (int *)malloc(100);free(p);free(p);//重复释放
}

6、动态开辟内存忘记释放(内存泄漏)

void test()
{int *p = (int *)malloc(100);if(NULL != p){*p = 20;}
}
int main()
{test();while(1);
}

注意:忘记释放不再使用的动态开辟的空间会造成内存泄漏。 切记: 动态开辟的空间一定要释放,并且正确释放 。

四、几个经典的笔试题

例1:

void GetMemory(char* p)
{p = (char*)malloc(100);
}
void Test(void)
{char* str = NULL;GetMemory(str);strcpy(str, "hello world");printf(str);
}
int main()
{Test();
}

请问:运行Test函数之后会出现什么结果?
程序直接崩溃了!?
答案:核心问题就是子函数中的p是临时变量,改变它并不会改变主函数中的str。
在这里插入图片描述
例2:

局部变量的指针不能做返回值,因为函数内的空间在函数返回后就会释放掉

char *GetMemory(void)
{char p[] = "hello world";return p;
}
void Test(void)
{char *str = NULL;str = GetMemory();printf(str);
}

答案:GetMemory函数返回的地址无法正常使用
解析:此题考的是:局部变量的指针不能做返回值,因为函数内的空间在函数返回后就会释放掉这一点。

例3:
运行结果?


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();
}

答案:打印hello

例4:
运行结果?

void Test(void)
{char* str = (char*)malloc(100);strcpy(str, "hello");free(str);// Str那块地址虽然被释放掉了,还给了操作系统,但是它并不是空指针。if (str != NULL){strcpy(str, "world");printf(str);}//所以最终的结果会打印world
}int main()
{Test();
}

答案:打印world

五、柔性数组

1.什么是柔性数组?

也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。 C99 中,结构体中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。

例如:


struct Stu
{int i;int arr[] ;//柔性数组成员
};

2、柔性数组的特点

柔性数组的特点:

  • 结构中的柔性数组成员前面必须至少一个其他成员。
  • sizeof 返回的这种结构大小不包括柔性数组的内存。
  • 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

sizeof 返回的这种结构大小不包括柔性数组的内存:
举例:

typedef struct Stu
{int i;int arr[] ;//柔性数组成员
}Stu;int main()
{printf("%d\n",sizeof(Stu));return 0;
}

在这里插入图片描述

3、柔性数组的使用

int i = 0;
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
//业务处理
p->i = 100;
for(i=0; i<100; i++)
{p->a[i] = i;
}
free(p);

4、柔性数组的优势

typedef struct st_type
{int i;int *p_a;
}type_a;
type_a *p = malloc(sizeof(type_a));
p->i = 100;
p->p_a = (int *)malloc(p->i*sizeof(int));
//业务处理
for(i=0; i<100; i++)
{p->p_a[i] = i;
}
//释放空间
free(p->p_a);
p->p_a = NULL;
free(p);
p = NULL;

在这里插入图片描述
----------------------------------------------------------------------------------------------

好了,今天的分享就到这里了
如果对你有帮助,记得点赞👍+关注哦!
我的主页还有其他文章,欢迎学习指点。关注我,让我们一起学习,一起成长吧!
在这里插入图片描述

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

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

相关文章

ASUS华硕ZenBook 13灵耀U 2代U3300F笔记本UX333FN/FA原装出厂Win10系统工厂安装模式

系统自带所有驱动、出厂主题壁纸、系统属性华硕专属LOGO标志、Office办公软件、MyASUS华硕电脑管家等预装程序 下载链接&#xff1a;https://pan.baidu.com/s/1dK0vMZMECPlT63Rb6-jeFg?pwdbym5 所需要工具&#xff1a;16G或以上的U盘(非必需) 文件格式&#xff1a;HDI,SWP,O…

CDH 6.3.2升级Flink到1.17.1版本

CDH&#xff1a;6.3.2 原来的Flink&#xff1a;1.12 要升级的Flink&#xff1a;1.17.1 操作系统&#xff1a;CentOS Linux 7 一、Flink1.17编译 build.sh文件&#xff1a; #!/bin/bash set -x set -e set -vFLINK_URLsed /^FLINK_URL/!d;s/.*// flink-parcel.properties FLI…

R | R及Rstudio安装、运行环境变量及RStudio配置

R | R及Rstudio安装、运行环境变量及RStudio配置 一、介绍1.1 R介绍1.2 RStudio介绍 二、R安装2.1 演示电脑系统2.2 R下载2.3 R安装2.4 R语言运行环境设置&#xff08;环境变量&#xff09;2.4.1 目的2.4.2 R-CMD测试2.4.3 设置环境变量 2.5 R安装测试 三、RStudio安装3.1 RStu…

vue 实现弹出菜单,解决鼠标点击其他区域的检测问题

弹出菜单应该具有的功能&#xff0c;当鼠标点击其他区域时&#xff0c;则关闭该菜单。 问题来了&#xff0c;怎么检测鼠标点击了其他区域而不是当前菜单&#xff1f; 百度“JS检测区域外的点击事件”&#xff0c;会发现有很多方法&#xff0c;有递归检测父元素&#xff0c;有遍…

【JavaEE初阶】 计算机是如何工作的

文章目录 &#x1f332;计算机发展史&#x1f38b;冯诺依曼体系&#xff08;Von Neumann Architecture&#xff09;&#x1f38d;CPU 基本工作流程&#x1f4cc;逻辑门&#x1f388;电子开关 —— 机械继电器(Mechanical Relay)&#x1f388;门电路(Gate Circuit)NOT GATE&…

【强化学习】基础概念

1. Agent (智能体) 智能体是进行决策和学习的实体&#xff0c;它能感知环境的状态&#xff0c;并基于策略采取动作以影响环境。智能体的目标是通过与环境的交互获得最大化的累积奖励。 2. Environment (环境) 环境是智能体所处的外部系统&#xff0c;它与智能体交互。环境的…

【数据结构-图】图介绍

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

高数:第二章:一元函数微分学

文章目录 一、导数与微分1.导数的概念(1)导数的定义(2)左右导数(3)定理&#xff1a;可导与左右导数的关系(4)可导三要素(5)用导数定义判断可导性 2.微分的概念(1)微分的定义(2)微分与可导的关系 3.导数与微分的几何意义(1)导数 f ′ ( x 0 ) f(x_0) f′(x0​)的几何意义&#x…

1.6.C++项目:仿mudou库实现并发服务器之channel模块的设计

项目完整版在&#xff1a; 文章目录 一、channel模块&#xff1a;事件管理Channel类实现二、提供的功能三、实现思想&#xff08;一&#xff09;功能&#xff08;二&#xff09;意义&#xff08;三&#xff09;功能设计 四、代码&#xff08;一&#xff09;框架&#xff08;二…

python监控ES索引数量变化

文章目录 1, datafram根据相同的key聚合2, 数据合并&#xff1a;获取采集10,20,30分钟es索引数据脚本测试验证 1, datafram根据相同的key聚合 # 创建df1 > json {key:A, value:1 } {key:B, value:2 } data1 {key: [A, B], value: [1, 2]} df1 pd.DataFrame(data1)# 创建d…

【QT开发(6)】0926-QT 中加入 fastDDS 通信库的程序使用说明

在智能驾驶中&#xff0c;DDS有可能被广泛使用&#xff0c;因此推出这篇说明教程。 1、基于【QT开发&#xff08;5&#xff09;】教程的项目文档进行开发 2、安装DDS 查看《【eProsima Fast DDS&#xff08;1&#xff09;】安装eProsima Fast DDS》 至少安装: foonathan_m…

Sentinel学习(2)——sentinel的使用,引入依赖和配置 对消费者进行流控 对生产者进行熔断降级

前言 Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件&#xff0c;主要以流量为切入点&#xff0c;从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。 本篇博客介绍sentinel的使用&#x…

Source Insight 工具栏图标功能介绍

这篇文章并不介绍 Source Insight 的具体使用方法&#xff0c;这类教程网上有很多&#xff0c;这里只分析 Souce Insight 工具栏图标的功能。 文章目录 Source Insight 简介Souce Insight 工具栏文件操作新建&#xff08;CtrlN&#xff09;打开&#xff08;CtrlO&#xff09;保…

35 LRU缓存

LRU缓存 题解1 双map&#xff08;差2个testcases&#xff09;题解2 哈希表双向链表&#xff08;参考&#xff09;题解3 STL:listunordered_map 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正…

无人直播间

失败&#xff01;&#xff01; 采用 ffmpeg 技术进行推流 推流代码&#xff1a; 【需要将rtmp替换为你的推流地址】 ffmpeg -re -stream_loop -1 -i "rain.mp4" -c copy -f flv ""推流地址获取 以哔哩哔哩为例 点击下方链接 开播设置 - 个人中心 - …

CCF CSP认证 历年题目自练Day17

CCF CSP认证 历年题目自练Day17 题目一 试题编号&#xff1a; 201803-1 试题名称&#xff1a; 跳一跳 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 256.0MB 问题描述&#xff1a; 问题描述   近来&#xff0c;跳一跳这款小游戏风靡全国&#xff0c;受到不少玩家的喜爱…

小黑子的java项目开发理解

小黑子的理解 一、基于Maven模板构建的三种常见Java项目——基于maven二、通常的java目录结构utils层 工具包model层&#xff08;pojo层&#xff09;exceptions层 报错包dao层&#xff08;mapper层&#xff09;[impl包—查询数据库]service层 定义接口 [impl—实现事务]control…

Backblaze发布2023中期SSD故障数据质量报告

作为一家在2021年在美国纳斯达克上市的云端备份公司&#xff0c;Backblaze一直保持着对外定期发布HDD和SSD的故障率稳定性质量报告&#xff0c;给大家提供了一份真实应用场景下的稳定性分析参考数据。 本文我们主要看下Backblaze最新发布的2023中期SSD相关故障稳定性数据报告。…

施耐德电气:勾勒未来工业愿景,赋能中国市场

9月19日&#xff0c;第23届中国国际工业博览会&#xff08;简称“工博会”&#xff09;在上海隆重召开。作为全球能源管理和自动化领域的数字化转型专家&#xff0c;施耐德电气在工博会现场全方位展现了自身对未来工业的全新视野与深刻见解&#xff0c;不仅展示了其贯通企业设计…

ubuntu 18.04安装libjasper-dev 亲测可行

情况&#xff1a; ubuntu 18.04 LTS安装OpenCV 3.4.16之前&#xff0c;需要安装几个依赖项&#xff1a; sudo apt-get install build-essential sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev sudo apt-get instal…