C/C++——内存管理

1.为什么存在动态内存分配

灵活性

静态内存分配是在编译时确定的,程序执行过程中无法改变所分配的内存大小;动态内存分配可以根本程序的运行环境来动态分配和释放空间,提供了更大的灵活性

动态数据结构

有些数据结构的大小和结构在编译时无法确定,需要在运行通过动态内存分配来创建和操作

资源的高效利用

通过动态内存分配,程序可以根据实际需求分配合适的内存空间,避免了静态内存分配中可能出现的浪费内存的问题。动态内存分配可以提高内存的利用率,允许多个对象共享内存空间。

编译灵活性

可以根据程序的运行状态来决定内存的分配和释放,使得程序具有更大的灵活性。

2.C/C++内存分布

请添加图片描述

栈区(stack)

在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内容容量有限,栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等

内存映射段

是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存,做进程间通信

堆区(heap)

一般由程序员分配释放,若程序员不释放,程结束时可能由OS回收,分配方式类似于链表

数据段(静态区)

存放全局变量、静态变量。程序结束后由系统释放

代码段

存放函数体(类成员函数和全局函数)的二进制

3.C语言内存管理方式

3.1malloc和free

函数原型如下:

void* malloc (size_t size);

malloc函数向内存申请一块连续可用的空间,并返回指向这块空间的指针

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

free函数用来释放动态开辟的内存

  • 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的
  • 如果参数ptr是NULL指针,则函数什么事情都不做
#include<stdio.h>
#include<stdlib.h>
int main()
{int num = 10;int* ptr = NULL;ptr = (int*)malloc(num * sizeof(int));//动态开辟10个int类型的内存空间if (NULL != ptr)						//判断ptr是否为空,{int i = 0;for (i = 0; i < num; i++){*(ptr + i) = 0;printf("%d ", *ptr);}}free(ptr);ptr = NULL;return 0;
}

3.2calloc

函数原型如下:

void* calloc (size_t num, size_t size);
  • 函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0
  • 与函数malloc的区别只在于calloc会有返回地址之前把之前申请的空间的每个字节初始化为全0
#include<stdio.h>
#include<stdlib.h>
int main()
{int* p = (int*)calloc(10, sizeof(int));if (NULL != p){for (int i = 0; i <10; i++){*(p + i) = i+1;printf("%d ", *(p+i));}}free(p);p = NULL;return 0;
}

3.3realloc

realloc函数的出现让动态内存管理更加灵活,可以做到对动态开辟内存大小的调整。

函数原型如下:

void* realloc (void* ptr, size_t size);
  • ptr是要调整的内存地址
  • size调整之后新大小
  • 返回值为调整之后的内存起始地址
  • 这个函数调整原内存空间大小的基础上,还会将原来的内存中的数据移动到新的空间

realloc在调整内存空间存在的两种情况:

1.原空间之后有足够大的空间——要扩展空间就直接在原有内存之后直接追加空间,原来空间的数据不发生变化

2.原空间之后没有足够大的空间——原空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用

#include <stdio.h>
#include <stdlib.h>int main()
{int* p = (int*)malloc(5 * sizeof(int)); // 分配5个整型大小的内存块if (p == NULL) {printf("Memory allocation failed.\n");return -1;}for (int i = 0; i < 5; i++) {p[i] = i + 1;  // 给每个元素赋值printf("%d ", p[i]);}printf("\n");int* new_p = (int*)realloc(p, 10 * sizeof(int)); // 将内存块大小重新分配为10个整型大小if (new_p == NULL) {printf("Memory reallocation failed.\n");free(p); // 重新分配失败,释放原先分配的内存块return -1;}for (int i = 0; i < 10; i++) {new_p[i] = i + 1;  // 给每个元素赋值printf("%d ", new_p[i]);}printf("\n");free(new_p); // 释放重新分配的内存块new_p = NULL;return 0;
}

4.C++内存管理方式

4.1new和delete操作内置类型

申请和释放单个元素的空间,使用newdelete操作符,申请和释放连续的空间,使用new[]delete[](注意:匹配起来使用)

void Test()
{// 动态申请一个int类型的空间int* ptr4 = new int;// 动态申请一个int类型的空间并初始化为10int* ptr5 = new int(10);// 动态申请10个int类型的空间int* ptr6 = new int[10];delete ptr4;delete ptr5;delete[] ptr6;
}

4.2operator new和operator delete函数

newdelete是用户进行动态内存申请和释放的操作符,operator newoperator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。
*/
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{// try to allocate size bytesvoid* p;while ((p = malloc(size)) == 0)if (_callnewh(size) == 0){// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void* pUserData)
{_CrtMemBlockHeader* pHead;RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));if (pUserData == NULL)return;_mlock(_HEAP_LOCK); /* block other threads */__TRY/* get a pointer to memory block header */pHead = pHdr(pUserData);/* verify block type */_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));_free_dbg(pUserData, pHead->nBlockUse);__FINALLY_munlock(_HEAP_LOCK); /* release other threads */__END_TRY_FINALLYreturn;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

通过上述两个全局函数的实现知道,operator new实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete最终是通过free来释放空间的

4.3new和delete的实现原理

4.3.1内置类型

如果申请的是内置类型的空间,newmallocdeletefree基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]delete[]申请的连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL

4.3.2自定义类型

  • new的原理
    • 调用operator new函数申请空间
    • 在申请的空间上执行构造函数,完成对象的构造
  • delete的原理
    • 在空间上执行析构函数,完成对象中资源的清理工作
    • 调用operator delete函数释放对象的空间
  • new T[N]的原理
    • 调用operator new[]函数,在operator new[]实际调用operator new函数完成N个对象空间的申请
    • 在申请的空间上执行N此构造函数
  • delete[]的原理
    • 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
    • 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

5.malloc/free和new/delete的区别

5.1共同点

都是从堆上申请空间,并且需要用户手动释放

5.2不同点

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不需要初始化,new可以初始化
  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
  4. malloc的返回值为void*,在使用时必须强转,new不需要,因为new后跟的是空间的类型
  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数和析构函数,而new在申请后会调用析构函数完成对象的初始化,delete在释放空间钱会调用析构函数完成空间中资源的清理

6.常见的动态内存错误

6.1对NULL指针的解引用操作

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

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

//c
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);
}
//c++
void test()
{int i = 0;int* p = new int[10];if (nullptr == p){exit(EXIT_FAILURE);}for (i = 0; i <= 10; i++){*(p + i) = i; // 当i是10的时候越界访问}delete[] p;
}

6.3对非动态开辟内存进行释放

//c
void test()
{int a = 10;int *p = &a;free(p);
}
//c++
void test()
{int a = 10;int *p = &a;delete p;
}

6.4释放一块动态开辟内存的一部分

//c
void test()
{int *p = (int *)malloc(100);p++;free(p);//p不再指向动态内存的起始位置
}
//c++
void test()
{int* p = new int[100];p++;delete[] p; //p不再指向动态内存的起始位置
}

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

//c
void test()
{int *p = (int *)malloc(100);free(p);free(p);//重复释放
}
//c++
void test()
{int *p = (int *)malloc(100);delete[] p;delete[] p;//重复释放
}

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

//c
int* p1 = (int*)malloc(sizeof(int));
//c++
int* p2 = new int;

6.6.1什么是内存泄漏?

内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内
存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对
该段内存的控制,因而造成了内存的浪费 。

6.6.2内存泄漏的危害?

长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现
内存泄漏会导致响应越来越慢,最终卡死 。

6.6.3如何避免内存泄漏?

  1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下一条智能指针来管理才有保证。
  2. 采用RAII思想或者智能指针来管理资源。
  3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
  4. 出问题了使用内存泄漏工具检测。不过很多工具都不够靠谱,或者收费昂贵。

6.6.1什么是内存泄漏?

内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内
存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对
该段内存的控制,因而造成了内存的浪费 。

6.6.2内存泄漏的危害?

长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现
内存泄漏会导致响应越来越慢,最终卡死 。

6.6.3如何避免内存泄漏?

  1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下一条智能指针来管理才有保证。
  2. 采用RAII思想或者智能指针来管理资源。
  3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
  4. 出问题了使用内存泄漏工具检测。不过很多工具都不够靠谱,或者收费昂贵。

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

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

相关文章

隐式意图和Activity启动模式:实现文件打开应用【Android、隐式意图】

隐式意图和Activity启动模式&#xff1a;实现文件打开应用 在Android开发中&#xff0c;隐式意图和Activity启动模式是两个重要的概念。它们可以用于实现不同应用之间的协作和交互。在本篇博客中&#xff0c;我们将探讨如何创建一个Android应用&#xff0c;该应用可以从外部应…

IEEE802系列协议知识点总结

IEEE 802 协议包含了以下多种子协议。把这些协议汇集在一起就叫IEEE 802 协议集。 (1)IEEE802.1 IEEE 802.1协议提供高层标准的框架&#xff0c;包括端到端协议、网络互连、网络管理、路由选择、桥接和性能测量。 •IEEE 802.1d:生成树协议(Spanning Tree Protocol&#xff0c…

C++笔记之不同buffer数量下的生产者-消费者机制

C笔记之不同buffer数量下的生产者-消费者机制 文章目录 C笔记之不同buffer数量下的生产者-消费者机制0.在不同的缓冲区数量下&#xff0c;生产者-消费者机制的实现方式和行为的区别1.最简单的生产者-消费者实现&#xff1a;抄自 https://mp.weixin.qq.com/s/G1lHNcbYU1lUlfugXn…

基于虚拟阻抗的下垂控制——孤岛双机并联Simulink仿真

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Oracle Database Express Edition (XE)配置与部署

获取下载安装包 https://www.oracle.com/cn/database/technologies/xe-downloads.htmlhttps://yum.oracle.com/repo/OracleLinux/OL7/latest/x86_64/index.html安装.rpm安装包 cd /usr/local/src wget https://download.oracle.com/otn-pub/otn_software/db-express/oracle-d…

Vue中如何进行分布式搜索与全文搜索(如Elasticsearch)

在Vue中实现分布式搜索与全文搜索&#xff08;使用Elasticsearch&#xff09; 分布式搜索和全文搜索在现代应用程序中变得越来越重要&#xff0c;因为它们可以帮助用户快速查找和检索大量数据。Elasticsearch是一种强大的分布式搜索引擎&#xff0c;它可以用于实现高性能的全文…

Gmail 将停止支持基本 HTML 视图

根据 Google 支持文档的更新内容&#xff0c;Gmail 将从明年 1 月起停止支持基本 HTML 视图。 ▲ Gmai 基本 HTML 视图界面 目前网页版 Gmail 提供两个界面&#xff1a;基本 HTML 视图和标准视图。停止支持基本 HTML 视图后&#xff0c;当前打开经典模式的基本 HTML 视图模式 …

苹果签名的MDM(Mobile Device Management)?是怎么做的?优势是什么?什么场合需要应用到?

苹果签名有多少种类之TF签名(TestFlight签名&#xff09;是什么&#xff1f;优势是什么&#xff1f;什么场合需要应用到&#xff1f; 苹果签名有多少种类之TF签名(TestFlight签名&#xff09;是什么&#xff1f;优势是什么&#xff1f;什么场合需要应用到&#xff1f; MDM&am…

基于Matlab求解高教社杯数学建模竞赛(cumcm2010A题)-储油罐的变位识别与罐容表标定(附上源码+数据+题目)

文章目录 题目解题源码数据下载 题目 通常加油站都有若干个储存燃油的地下储油罐&#xff0c;并且一般都有与之配套的“油位计量管理系统”&#xff0c;采用流量计和油位计来测量进/出油量与罐内油位高度等数据&#xff0c;通过预先标定的罐容表&#xff08;即罐内油位高度与储…

lv7 嵌入式开发-网络编程开发 11 TCP管理与UDP协议

目录 1 TCP管理 1.1 三次握手 1.2 四次挥手 1.3 保活计时器 2 wireshark安装及实验 3.1 icmp协议抓包演示 3.2 tcp协议抓包演示 3 UDP协议 3.1 UDP 的主要特点&#xff1a; 4 练习 1 TCP管理 1.1 三次握手 TCP 建立连接的过程叫做握手。 采用三报文握手&#xff1…

机器学习笔记 - 深入研究spaCy库及其使用技巧

一、简述 spaCy 是一个用于 Python 中高级自然语言处理的开源库。它专为生产用途而设计,这意味着它不仅功能强大,而且快速高效。spaCy 在学术界和工业界广泛用于各种 NLP 任务,例如标记化、词性标注、命名实体识别等。 安装,这里使用阿里的源。 pip install spacy…

2023版 STM32实战6 输出比较(PWM)包含F407/F103方式

输出比较简介和特性 -1-只有通用/高级定时器才能输出PWM -2-占空比就是高电平所占的比例 -3-输出比较就是输出不同占空比的信号 工作方式说明 -1-1- PWM工作模式 -1-2- 有效/无效电平 有效电平可以设置为高或低电平&#xff0c;是自己配置的 周期选择与计算 周期重…

华为云云耀云服务器L实例评测|安装搭建学生成绩管理系统

1.前言概述 华为云耀云服务器L实例是新一代开箱即用、面向中小企业和开发者打造的全新轻量应用云服务器。多种产品规格&#xff0c;满足您对成本、性能及技术创新的诉求。云耀云服务器L实例提供丰富严选的应用镜像&#xff0c;实现应用一键部署&#xff0c;助力客户便捷高效的在…

SRT服务器SLS

目前互联网上的视频直播有两种&#xff0c;一种是基于RTMP协议的直播&#xff0c;这种直播方式上行推流使用RTMP协议&#xff0c;下行播放使用RTMP&#xff0c;HTTPFLV或者HLS&#xff0c;直播延时一般大于3秒&#xff0c;广泛应用秀场、游戏、赛事和事件直播&#xff0c;满足了…

mybatis项目启动报错:reader entry: ���� = v

问题再现 解决方案一 由于指定的VFS没有找&#xff0c;mybatis启用了默认的DefaultVFS&#xff0c;然后由于DefaultVFS的内部逻辑&#xff0c;从而导致了reader entry乱码。 去掉mybatis配置文件中关于别名的配置&#xff0c;然后在mapper.xml文件中使用完整的类名。 待删除的…

JMETER自适应高分辨率的显示器

系列文章目录 历史文章 每天15分钟JMeter入门篇&#xff08;一&#xff09;&#xff1a;Hello JMeter 每天15分钟JMeter入门篇&#xff08;二&#xff09;&#xff1a;使用JMeter实现并发测试 每天15分钟JMeter入门篇&#xff08;三&#xff09;&#xff1a;认识JMeter的逻辑控…

【Pandas】数据分组groupby

本文目标&#xff1a; 应用groupby 进行分组对分组数据进行聚合,转换和过滤应用自定义函数处理分组之后的数据 文章目录 1. 数据聚合1.1 单变量分组聚合1.2 Pandas内置聚合方法1.3 聚合方法使用Numpy的聚合方法自定义方法同时计算多种特征向agg/aggregate传入字典 2. 数据转换…

DHCPsnooping 配置实验(2)

DHCP报文泛洪攻击 限制接收到报文的速率 vlan 视图或者接口视图 dhcp request/ dhcp-rate dhcp snooping check dhcp-request enable dhcp snooping alarm dhcp-request enable dhcp snooping alarm dhcp-request threshold 1 超过则丢弃报文 查看[Huawei]dis dhcp statistic…

​苹果应用高版本出现:“无法安装此app,因为无法验证其完整性”是怎么回事?竟然是错误的?

最近经常有同学私聊我问苹果应用签名后用落地页下载出现高版本是什么意思&#xff1f;我一脸懵&#xff01;还有这个操作&#xff1f;高版本是个啥玩意&#xff01;所以我就上了一下科技去搜索引擎搜索了下&#xff0c;哈哈哈&#xff0c;然后了解下来发现是这样的首先我们确定…

Qt扩展-Advanced-Docking 简介及配置

Advanced-Docking 简介及配置 一、概述二、项目结构三、安装配置四、代码测试 一、概述 Advanced-Docking 是类似QDockWidget 功能的多窗口停靠功能的库。很像visual stdio 的 停靠功能&#xff0c;这个库对于停靠使用的比较完善。很多的软件都使用了这个框架。 项目源地址&a…