【C++】C/C++内存管理

前言:

        前面我们已经学习了类与对象,认识了六个默认成员函数。这一篇文章我们来学习C/C++内存管理,深入了解这套机制有利于我们之后写出更好的C/C++程序。

一、C/C++内存分布:

1.C/C++中程序内存区域划分:

         在C++中,内存划分为六个部分,分别是:内核空间、栈、内存映射段、堆、数据段、代码段。

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

        堆,就是哪些有new分配的内存块,它们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果没有释放就会存在内存泄漏,只能等程序结束后,操作系统自动回收。

        数据段(全局/静态存储区),全局变量和静态变量被分配到这一块内存中,在C语言中,全局变量由分为初始化和未初始化的,在C++里面没有这个区分了,他们里面存放的是常量,不允许修改。

        代码段(常量存储区),这是一块比较特殊的存储区,存放的是常量,不允许被修改。

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

2.经典代码分析:

        我们通过上面讲到的六个内存区域,来判断一下代码变量存储的内存区域。

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
    static int staticVar = 1;
    int localVar = 1;
    int num1[10] = { 1, 2, 3, 4 };
    char char2[] = "abcd";
    const char* pChar3 = "abcd";
    int* ptr1 = (int*)malloc(sizeof(int) * 4);
    int* ptr2 = (int*)calloc(4, sizeof(int));
    int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
    free(ptr1);
    free(ptr3);
}

globalVar在哪里?____
staticVar在哪里?____
num1 在哪里?____

staticGlobalVar在哪里?____
localVar在哪里?____


char2在哪里?____
pChar3在哪里?____
ptr1在哪里?____

*char2在哪里?___
*pChar3在哪里?____
*ptr1在哪里?____
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)

globalVar是一个全局变量,staticVar是一个静态局部变量,staticGlobalVar是一个静态全局变量,所以这三个变量都放在数据段(静态区)上;

num1[10]和char2都是数组,localVar是一个函数内部定义的变量,char所以这三个是放在上的;

pChar3是一个局部的指针变量,也是存在上的;*pChar3指向常量字符串,解引用后就是在常量区

ptr1是指针变量,是放在上的;*ptr1是malloc出来的,所以是在上的。

二、内存管理方式:

1.C语言中动态内存管理方式:malloc/calloc/realloc/free

void Test()
{
    int* p1=(int*)malloc(sizeof(int));
    free(p1);
    int* p2=(int*)calloc(4,sizeof(int));
    int* p3=(int*)realloc(p2,sizeof(int)*10);

    free(p3);
}

①malloc函数:

malloc的全称是memory allocation,中文叫动态内存分配,用于申请一块连续的指定大小的内存块区域以viod*类型返回分配的内存区域地址。

返回值:返回的指针指向该分配域的开头位置,否则返回空指针NULL;

②calloc函数:

calloc也是用于申请一块连续的致电给大小的内存块区域。与malloc最大的区别就是calloc会初始化为0;

③realloc函数:

指针名=(类型*)realloc(要改变的内存大小的指针名,新的大小)。

新的的大小可以比原来的大也可以比原来的小(如果比原来的大则新分配的部分不会初始化,如果比原来的小则可能会导致数据丢失)

2.C++内存管理方式:

        我们在C语言学习的内存管理方式可以在C++继续使用,但有些地方使用起来就会无能为力或者会比较麻烦,所以C++又推出了自己的内存管理方式:通过new和delete草错付进行动态内存管理

①new/delete操作内置类型:

void test()
{
    //动态申请一个int类型的空间
    int* ptr1=new int;

    //动态申请一个int类型的空间并初始化为10
    int* ptr2=new int(10);    //10为初始化值

    //动态申请10个int类型的空间
    int* ptr3=new int[10];   //10为对象个数;
    
    //动态申请10个int类型的空间,并通过花括号初始化
    int* ptr4=new int[10]{1};
    
    delete ptr1;
    delete ptr2;
    delete[] ptr3;
    delete[] ptr4;
}

ps:

①ptr3是new了10个对象,不初始化。ptr4是new了10个对象,然后通过花括号初始了第一个值是10,其余的编译器自动初始化为0,如果你想继续初始剩下的值可以在花括号继续写下去,写法跟C语言数组初始化一样,比如你想把第二、三个分别初始化为2跟3,就这样写{1,2,3}即可。

②申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[]。(匹配使用)

②new和delete操作自定义类型

class A
{
public:
    A(int a = 0)
        :_a(a)
    {
        cout << "A():" << this << endl;
    }
    ~A()
    {
        cout << "~A():" << this << endl;
    }
private:
    int _a;
};
int main()
{
    //new/delete和malloc/free最大的区别是new/delete对于自定义类型除了开空间还会调用构造函数和析构函数。
    A* p1 = (A*)malloc(sizeof(A));
    A* p2 = new A(1);
    free(p1);
    delete p2;


    int* p3 = (int*)malloc(sizeof(int));
    int* p4 = new int;
    free(p3);
    delete p4;

    A* p5 = (A*)malloc(sizeof(A) * 10);
    A* p6 = new A[10];
    free(p5);
    delete[] p6;

    return 0;

}

注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。

三、operator new与operator delete函数:

        new和delete是用户进行动态内存申请和释放的操作符,operator new和operator delete是系统提供的全局函数。注意:operator new和operator delete不是对new和delete重载,这是两库函数。new在底层中实际通过调用operator new全局函数来实现申请空间,相同的delete底层也是通过operator delete全局函数来实现释放空间;

operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功是直接返回,申请失败,尝试执行空间不足应对措施,如果该应对措施用户设置了,则继续申请,否则抛异常。operator new底层实际是对malloc进行封装,operator delete底层实际是对free进行封装。

int main()
{
    Stack* s1 = (Stack*)operator new(sizeof(Stack));
    operator delete(s1);

    Stack* s2 = (Stack*)malloc(sizeof(Stack));
    free(s2);

    return 0;
}

operator new和operator delete的功能分别跟malloc和free一样,都不会去调用构造函数和析构函数,但是还是有不同的:1、operator new不需要检查开辟空间的合法性;2、operator new开辟失败会抛异常。

1.重载operator new与operator delete(了解):

一般情况下不需要对 operator new 和 operator delete进行重载,除非在申请和释放空间
时候有某些特殊的需求。比如:在使用new和delete申请和释放空间时,打印一些日志信息,可以简单帮助用户来检测是否存在内存泄漏。

四、new和delete实现原理:

1.内置类型:

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

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来释
放空间。
 

3. 定位new表达式(placement-new) (了解):

         定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式:                    
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表
使用场景:
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。

四、内存泄漏:

1.什么是内存泄漏:

        内存泄漏早在C语言阶段我们就了解过了,这边我们重新了解一下。内存泄漏是指因为错误或疏忽导致程序未能释放已经不在使用的内存的情况。内存泄漏并不是指内存物理上的泄漏丢失,而是程序分配某内存后,因为设计错误,失去对这块内存的控制,因而造成内存浪费。

内存泄漏的危害

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

2.内存泄漏分类:

Ⅰ.堆内存泄漏:

堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一
块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分
内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。

Ⅱ.系统资源泄漏:

指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放
掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

3.如何避免内存泄漏:

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

多要素环境监测一体机-生态环境的守护者

随着人类活动的不断增加&#xff0c;环境问题日益凸显。为了实时了解环境状况&#xff0c;保护生态环境&#xff0c;一款多要素环境监测一体机应运而生。 一、实时监测&#xff0c;掌握环境动态 WX-CSQX12 多要素环境监测一体机能够实时监测空气质量、温湿度、噪音、风速等多…

SSM项目实战-前端-添加分页控件-调正页面布局

1、Index.vue <template><div class"common-layout"><el-container><el-header><el-row><el-col :span"24"><el-button type"primary" plain click"toAdd">新增</el-button></el-…

华清远见嵌入式学习——C++——作业3

作业要求&#xff1a; 代码&#xff1a; #include <iostream>using namespace std;class Per { private:string name;int age;double *high;double *weight; public://有参构造函数Per(string n,int a,double h,double w):name(n),age(a),high(new double(h)),weight(ne…

CoreDNS实战(一)-构建高性能、插件化的DNS服务器

1 概述 在企业高可用DNS架构部署方案中我们使用的是传统老牌DNS软件Bind, 但是现在不少企业内部流行容器化部署&#xff0c;所以也可以将Bind替换为 CoreDNS &#xff0c;由于 CoreDNS 是 Kubernetes 的一个重要组件&#xff0c;稳定性不必担心&#xff0c;于此同时还可将K8S集…

QT之QString

QT之QString 添加容器 点击栅格布局 添加容器&#xff0c;进行栅格布局 布局总结&#xff1a;每一个模块放在一个Group中&#xff0c;排放完之后&#xff0c;进行栅格布局。多个Group进行并排时&#xff0c;先将各个模块进行栅格布局&#xff0c;然后都选中进行垂直布…

Python中对数组连续赋值的问题

问题描述 在python中&#xff0c;首先用两个等号对两个数组进行初始化并赋值。之后&#xff0c;对任何一个数组进行赋值&#xff0c;都会将其赋予相同值。 import numpy as np Array1 Array2 np.empty(2) Array1[0],Array2[0]70,80 print(Array1[0],Array2[0])80.0 80.0 …

Learning Normal Dynamics in Videos with Meta Prototype Network 论文阅读

文章信息&#xff1a;发表在cvpr2021 原文链接&#xff1a; Learning Normal Dynamics in Videos with Meta Prototype Network 摘要1.介绍2.相关工作3.方法3.1. Dynamic Prototype Unit3.2. 视频异常检测的目标函数3.3. 少样本视频异常检测中的元学习 4.实验5.总结代码复现&a…

【电机控制】PMSM无感foc控制(六)相电流检测及重构 — 双电阻采样、三电阻采样

0. 前言 目前&#xff0c;永磁同步电机的电流信号采样方法应用较多的是分流电阻采样&#xff0c;包括单电阻、双电阻以及三电阻采样法。其中&#xff0c;单电阻采样上一章节已经讲解&#xff0c;这章讲双电阻以及三电阻电流采样法。 1. 双电阻采样 1.1 双电阻采样原理 双电阻采…

FPGA时序分析与时序约束(一)

一、为什么要进行时序分析和时序约束 PCB通过导线将具有相关电气特性的信号相连接&#xff0c;这些电气信号在PCB上进行走线传输时会产生一定的传播延时。 而FPGA内部也有着非常丰富的可配置的布线资源&#xff0c;能够让位于不同位置的逻辑资源块、时钟处理单元、BLOCK RAM、D…

线性回归 numpy实现线性回归

手写线性回归 使用numpy随机生成数据 import numpy as np import matplotlib.pyplot as plt# 生成模拟数据 np.random.seed(42) X 2 * np.random.rand(200, 1) y 4 3 * X np.random.randn(200, 1)# 可视化数据 plt.scatter(X, y) plt.xlabel(X) plt.ylabel(y) plt.title(…

MFC发送ZPL指令控制斑马打印机

1、参考1&#xff1a;用Python操控斑马打印机的技术总结 - 重拾初心的青年人 - 博客园 (cnblogs.com) 参考2&#xff1a;VC斑马打印机_vc zpl-CSDN博客 参考3&#xff1a;斑马打印机ZPL语言编程实战_梅长酥的博客-CSDN博客 参考4&#xff1a;关于斑马打印机开发的几种方式_斑马…

人工智能的新篇章:深入了解大型语言模型(LLM)的应用与前景

项目设计集合&#xff08;人工智能方向&#xff09;&#xff1a;助力新人快速实战掌握技能、自主完成项目设计升级&#xff0c;提升自身的硬实力&#xff08;不仅限NLP、知识图谱、计算机视觉等领域&#xff09;&#xff1a;汇总有意义的项目设计集合&#xff0c;助力新人快速实…

pbootcms建站

pbootcms建站 一、下载pbootcms二、安装1、进入宝塔面在网站栏&#xff0c;新建站点&#xff0c;将该址里面文件全部清再将下载的pbootcms上传至该地址。 三、修改关联数据库1、在根目录下/config打开database.php照如下修改这里我使用mysqli数据库。修改并使用自已创建的数据库…

JAVA-作业7-画一个笑脸

要求如题 代码如下&#xff1a; SmileFace01: import java.awt.Color; import java.awt.Graphics;import javax.swing.JPanel;public class SmileFace01 extends JPanel {Overrideprotected void paintComponent(Graphics g) {super.paintComponent(g);int width getWidth(…

基于springboot+vue的景区民宿预约系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

代码随想录day5 哈希表part 01 242.有效的字母异位词 349. 两个数组的交集 202. 快乐数 1. 两数之和

当我们遇到了要快速判断一个元素是否出现集合里的时候&#xff0c;就要考虑哈希法。 哈希碰撞&#xff1a;1、拉链法&#xff1a;其实拉链法就是要选择适当的哈希表的大小&#xff0c;这样既不会因为数组空值而浪费大量内存&#xff0c;也不会因为链表太长而在查找上浪费太多时…

Stable Diffusion AI绘画系列【13】:毛茸茸的可爱动物们

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

cmd查看进程信息 终止进程

cmd查看进程信息 终止进程 1、cmd查看进程信息2、终止进程 1、cmd查看进程信息 tasklist命令 描述: 该工具显示在本地或远程机器上当前运行的进程列表。 tasklist /?查看本机所有进程列表 tasklist /V根据进程名 查看jmeter进程 tasklist /V |findstr /i jmeter2、终止进程…

分享全球顶尖的AIGC文生图资源

1 引言 人工智能正在改变许多行业的格局&#xff0c;而其中改变最直观和影响最大的就是AIGC领域的图像创作。文生图技术作为AIGC的一个重要分支&#xff0c;展现了人工智能在视觉创作领域的巨大潜力。发展至今已经有很多AI文生图平台&#xff0c;这是一次革命性的突破&#xf…

C++实现顺序栈的基本操作(扩展)

#include <stdio.h> typedef char ElemType; #define StackSize 100 /*顺序栈的初始分配空间*/ typedef struct { ElemType data[StackSize]; /*保存栈中元素*/int top; /*栈顶指针*/ } SqStack; void InitStack(SqStack &st) {st.top-1; } …