C/C++内存管理(含C++中new和delete的使用)

news2024/11/25 19:48:19

文章目录

  • C/C++内存管理(含C++中new和delete的使用)
    • 1、C/C++内存分布
    • 2、C语言中动态内存管理方式:malloc/calloc/realloc/free
    • 3、C++动态内存管理
      • 3.1、new/delete操作内置类型
      • 3.2、new/delete操作自定义类型
    • 4、operator new与operator delete函数
    • 5、new和delete的实现原理
      • 5.1、内置类型
      • 5.2、自定义类型
    • 6、定位new表达式(placement-new)

img

C/C++内存管理(含C++中new和delete的使用)

1、C/C++内存分布

我们先来看下面的一段代码和相关问题。

int globalVar = 1;
static int staticGlobalVar = 1;

int main() {
    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);
    return 0;
}
//1. 选择题:
// 选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
// globalVar在哪里?____  staticGlobalVar在哪里?____
// staticVar在哪里?____  localVar在哪里?____
// num1 在哪里?____
//
// char2在哪里?____  *char2在哪里?___
// pChar3在哪里?____   *pChar3在哪里?____
// ptr1在哪里?____ *ptr1在哪里?____
//2. 填空题:
// sizeof(num1) = ____; 
//sizeof(char2) = ____;  strlen(char2) = ____;
// sizeof(pChar3) = ____;   strlen(pChar3) = ____;
// sizeof(ptr1) = ____;
//3. sizeof 和 strlen 区别?
//

  • 说明
    1. 又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。
    2. 内存映射段 是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(Linux课程如果没学到这块,现在只需要了解一下)
    3. 用于程序运行时动态内存分配,堆是可以上增长的。
    4. 数据段存储全局数据和静态数据
    5. 代码段可执行的代码/只读常量

所以1.选择题有答案了。

那么2.填空题答案是:之前学过不讲了哈哈。

其中3.sizeofstrlen 区别?

答:strlen遇到\0结束,strlen算字符串长度,sizeof算变量大小。


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

malloccallocrealloc 是在C语言中用于动态内存分配的三个函数,它们有一些区别,主要体现在它们的功能和用法上。

  1. malloc(Memory Allocation,内存分配):

    • malloc 是 “memory allocation” 的缩写,用于分配指定大小的内存块。
    • 它只分配内存,不对内存进行初始化,所以分配的内存中可能包含任意值。
    void* malloc(size_t size);
    
  2. calloc(Contiguous Allocation,连续分配):

    • calloc 也是用于分配内存的函数,但与 malloc 不同的是,calloc 分配的内存块会被初始化为零
    • calloc 接受两个参数,分别是所需的元素个数和每个元素的大小。
    void* calloc(size_t num_elements, size_t element_size);
    
  3. realloc(Re-allocation,重新分配):

    • realloc 用于重新分配已分配内存的大小,可以用于扩大或缩小内存块的大小。
    • 如果原始内存块的地址不为空,realloc 会尝试在原始地址上修改内存块的大小(如果新内存大小大于这块原始空间,则还是需要重新开辟内存),如果原始地址为空,则行为类似于 malloc
    void* realloc(void* ptr, size_t new_size);
    

总结:

  • malloc 只分配指定大小的内存块,不进行初始化。
  • calloc 分配指定数量和大小的内存块,并将内存块的所有位初始化为零。
  • realloc 重新分配内存块的大小,可以用于扩大或缩小已分配内存的大小。
int main() {
    int *p1 = (int *) malloc(sizeof(int));
    free(p1);
// 1.malloc/calloc/realloc的区别是什么?
    int *p2 = (int *) calloc(4, sizeof(int));
    int *p3 = (int *) realloc(p2, sizeof(int) * 10);
// 这里需要free(p2)吗? --- 看情况
    free(p3);
    return 0;
}

3、C++动态内存管理

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力(比如给对象初始化),而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理

3.1、new/delete操作内置类型

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

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

    cout << *ptr5 << endl;
    // 动态申请10个int类型的空间
    int *ptr6 = new int[3];
    // 动态申请10个int类型的空间并初始化前3个
    int *ptr7 = new int[3]{1, 2, 3};
    cout << ptr7[0] << ptr7[1] << ptr7[2] << ptr7[3];
    
    delete ptr4;
    delete ptr5;
    delete[] ptr6;
    delete[] ptr7;
}
  • 初始化格式
    • 单个元素空间:new 类型 (初始化值)
    • 连续空间:new 类型 [元素个数] {从前往后元素初始化值,其余元素初始化为0}
  • 注意:申请和释放单个元素的空间,使用newdelete操作符,申请和释放连续的空间,使用new[]delete[]。注意:匹配起来使用

3.2、new/delete操作自定义类型

class Date {
public:
    Date() : _year(1), _month(1), _day(1) {
        cout << "Date()" << endl;
    }

    Date(int year, int month, int day) : _year(1), _month(1), _day(1) {
        _year = year;
        _month = month;
        _day = day;
        cout << "Date()" << endl;
    }

    ~Date() {
        cout << "~Date()" << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};


int main() {
    Date *ptr1 = new Date();
    Date *ptr2 = new Date(2, 2, 2);

    Date *ptr3 = new Date[10]{{1, 2, 2}};

    free(ptr1);
    delete ptr2;
    delete[] ptr3;
    return 0;
}

  • 初始化格式
    • 单个元素空间:new 类型 (初始化值)
    • 连续空间:new 类型 [元素个数] {从前往后元素初始化值使用{}代表每一个元素的值,其余元素初始化为0}
  • new/deletemalloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数

4、operator new与operator delete函数

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

  • operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
  • operator delete 最终是通过free来释放空间的。
class Date {
public:
    Date() : _year(1), _month(1), _day(1) {
        cout << "Date()" << endl;
    }

    Date(int year, int month, int day) : _year(1), _month(1), _day(1) {
        _year = year;
        _month = month;
        _day = day;
        cout << "Date()" << endl;
    }

    ~Date() {
        cout << "~Date()" << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};


int main() {
    //operator new -- 不调用构造函数 和 malloc 基本一样
    Date *ptr6 = (Date *) operator new(sizeof(Date));

    delete new(ptr6) Date;
    ptr6 = nullptr;
    return 0;
}

5、new和delete的实现原理

5.1、内置类型

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

5.2、自定义类型

  • new的原理

    1. 调用operator new函数申请空间
    2. 在申请的空间上执行构造函数,完成对象的构造
  • delete的原理

    1. 在空间上执行析构函数,完成对象中资源的清理工作
    2. 调用operator delete函数释放对象的空间

  • new T[N]的原理

    1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请

    2. 在申请的空间上执行N次构造函数

  • delete[]的原理

    1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理

    2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

这里我们需要注意一个现象对于内置类型在new T[N]时候,往往开辟的内存空间会大于N*sizeof(T),可能会多出4个字节

原本这里ptr2开辟的空间size应该是12*10 = 120,但是这里显示124,其中4个字节是用来存储开辟连续Date对象的个数。

class Date {
public:
    Date() : _year(1), _month(1), _day(1) {
        cout << "Date()" << endl;
    }

    Date(int year, int month, int day) : _year(1), _month(1), _day(1) {
        _year = year;
        _month = month;
        _day = day;
        cout << "Date()" << endl;
    }

    ~Date() {
        cout << "~Date()" << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};


int main() {
    Date* ptr1 = new Date;
    delete ptr1;

    Date* ptr2 = new Date[10];
    delete[] ptr2;
    return 0;
}

但是如果是内置类型new T[N],则不需要另外的空间存个数

原本这里ptr3开辟的空间size应该是4*10 = 40,并且这里显示40,即没有开辟空间存个数。

class Date {
public:
    Date() : _year(1), _month(1), _day(1) {
        cout << "Date()" << endl;
    }

    Date(int year, int month, int day) : _year(1), _month(1), _day(1) {
        _year = year;
        _month = month;
        _day = day;
        cout << "Date()" << endl;
    }

    ~Date() {
        cout << "~Date()" << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};


int main() {
    Date* ptr1 = new Date;
    delete ptr1;

    Date* ptr2 = new Date[10];
    delete[] ptr2;
  
  	int* ptr3 = new int[10];
		delete[] ptr3;
    return 0;
}

原因:这多出来的4个字节是用来记录开辟T大小连续空间的个数,以便于delete [] 进行析构和释放空间。

这里如果不写析构函数(默认成员变量没有开辟空间,如果开辟了空间,必须调用析构函数释放空间,不然会内存泄露)的话就不报错,因为成员变量都是内置类型没有开空间,不需要调用析构函数,也就不需要在前面添加4字节存个数,即自定义类型new T[N]可以直接delete,内置类型也一样。

class Date {
public:
    Date() : _year(1), _month(1), _day(1) {
        cout << "Date()" << endl;
    }

    Date(int year, int month, int day) : _year(1), _month(1), _day(1) {
//        _a = new int[10];
        _year = year;
        _month = month;
        _day = day;
        cout << "Date()" << endl;
    }

//    ~Date() {
//        cout << "~Date()" << endl;
//    }

private:
//    int* _a;
    int _year;
    int _month;
    int _day;
};


int main() {
    Date *ptr1 = new Date;
    delete ptr1;

    Date *ptr2 = new Date[10];
    // 这里如果不写析构函数(默认成员变量没有开辟空间,如果开辟了空间,必须调用析构函数释放空间,不然会内存泄露)的话就不报错
    // 因为成员变量都是内置类型没有开空间,不需要调用析构函数,也就不需要在前面添加4字节存个数
    delete ptr2;
    return 0;
}

原本这里ptr2开辟的空间size应该是12*10 = 120,并且这里显示120,即没有开辟空间存对象个数。


6、定位new表达式(placement-new)

定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象

  • 使用格式new (place_address) type或者new (place_address) type(initializer-list)
    • place_address必须是一个指针,initializer-list是类型的初始化列表
  • 使用场景
    • 定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
class Date {
public:
    Date() : _year(1), _month(1), _day(1) {
        cout << "Date()" << endl;
    }

    Date(int year, int month, int day) : _year(1), _month(1), _day(1) {
        _year = year;
        _month = month;
        _day = day;
        cout << "Date()" << endl;
    }

    ~Date() {
        cout << "~Date()" << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};


int main() {
    Date *p = (Date *) operator new(sizeof(Date));
    // 不能显式调用构造函数
    // p->Date();
    // 定位new可以显式调用构造函数
    new(p)Date(1, 1, 1);
    p->~Date();

    return 0;
}

OKOK,C/C++内存管理就到这里。如果你对Linux和C++也感兴趣的话,可以看看我的主页哦。下面是我的github主页,里面记录了我的学习代码和leetcode的一些题的题解,有兴趣的可以看看。

Xpccccc的github主页

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

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

相关文章

天眼销:B端销冠的私藏宝藏!

在B端销售的业务场景下&#xff0c;获取客户的联系方式是绕不开的一个话题&#xff0c;并且也有很多销售有自己的经验。 怎么去获取企业客户呢&#xff1f;你肯定想我得找到企业基本的信息还有联系方式&#xff0c;这时候你可能会想到去知名的查查平台。然后你会发现&#xff…

tomcat调优配置

一. 设置账户进入管理页面 通过浏览器进入Tomcat7的管理模块页面&#xff1a;http://localhost:8080/manager/status 按照提示&#xff0c;在Tomcat7服务器指定的位置修改配置文件&#xff08;conf/tomcat-users.xml&#xff09;&#xff0c;增加相应的用户和角色配置标签 <…

亲子开衫外套 I 真的好温柔好有气质

分享适合宝宝和麻麻 一起穿的开衫外套 包芯纱拼貂毛 软糯亲肤不扎人 上身体验感非常不错 这种面料还不易起球 质感满满&#xff0c;单穿内搭都可&#xff01;

SRGAN 使用指南:将低分辨率图像转换为高分辨率图像

SRGAN、ESRGAN、Real-ESRGAN 使用指南 SRGAN网络结构优化目标 ESRGANReal-ESRGAN SRGAN 超分辨率&#xff1a;从低分辨率(LR)图像来估计其对应高分辨率(HR)图像的任务&#xff0c;被称作超分辨率(SR)。 SRGAN 图像超分辨率的深度学习模型&#xff0c;通过生成对抗网络&#x…

如何保证缓存和数据库的双写一致性?

一、什么是数据库和缓存双写一致性&#xff1f; 在分布式系统中&#xff0c;数据库和缓存会搭配一起使用&#xff0c;以此来保证程序的整体查询性能。也就说&#xff0c;分布式系统为了缓解数据库查询的压力&#xff0c;会将查出来的数据保存在缓存中&#xff0c;下次再查询时…

【送书活动二期】Java和MySQL数据库中关于小数的保存问题

之前总结过一篇文章mysql数据库&#xff1a;decimal类型与decimal长度用法详解&#xff0c;主要是个人学习期间遇到的mysql中关于decimal字段的详解&#xff0c;最近在群里遇到一个小伙伴提出的问题&#xff0c;也有部分涉及&#xff0c;今天就再大致总结一下Java和MySQL数据库…

SpringCloudAlibaba之Nacos——详细讲解

目录 一、SpringCloudAlibaba简介 1. spring cloud alibaba 特点 2.springcloud 组件 二、环境搭建 1.构建项目并引入依赖 三、Nacos 1.什么是Nacos 2.安装Nacos 3.启动安装服务 4.访问nacos的web服务管理界面 四、开发服务注册到nacos 1.创建项目并引入依赖 2.配置注册地…

idea 导入外部包 打包失败

一、在项目中引入jar包 二、pom文件添加 引入外部包 <!--应用第三方包监听文件--><dependency><groupId>jnotify</groupId><artifactId>jnotify</artifactId><version>0.94.0</version><scope>system</scope><…

LDF文件之LDF Explorer工具

LDF Explorer工具 LDF文件比较像DBC文件&#xff0c;也是用来解析报文的&#xff0c;DBC文件是用在CAN通讯上的&#xff0c;LDF文件是用在LIN通讯上的。 我们可以用vector自带的工具LDF Explorer打开LDF文件&#xff0c;这个工具是最标准的&#xff0c;有些同学自己做了一个L…

西工大网络空间安全学院计算机系统基础实验零

首先&#xff0c;下载VMware17 Pro workstation。为什么要下载VMware17 Pro workstation呢&#xff1f;因为计算机系统基础实验有四个大部分&#xff1a;利用位运算实现诸如a*b&#xff0c;a/b&#xff0c;a*(2^4)等运算&#xff1b;C语言循环语句、switch语句等语句与汇编代码…

Linux基本指令总结(二)

1.man指令&#xff08;重要&#xff09; Linux的命令有很多参数&#xff0c;我们不可能全记住&#xff0c;我们可以通过查看联机手册获取帮助。访问Linux手册页的命令是 man 语法: man [选项] 命令 man指令就相当于一个精通linux的专家&#xff0c;你要查询的指令或者函数&…

正式版PS 2024 25新增功能 刚刚发布的虎标正式版

Adobe Photoshop 2024是一款业界领先的图像编辑软件&#xff0c;被广泛应用于设计、摄影、插图等领域。以下是这款软件的一些主要功能和特点&#xff1a; 丰富的工具和功能。Adobe Photoshop 2024提供了丰富的工具和功能&#xff0c;可以帮助用户对图像进行编辑、修饰和优化。…

嵌入式Linux学习(2)——经典CAN介绍(上)

目录 一. CAN与ISO-OSI Model 二. CAN通信 2.1 接线方式 2.1.1 闭环网络 2.1.2 开环网络 2.2 收发流程 2.2.1 发送 2.2.2 接收 三. CAN BUS访问与仲裁 3.1 “线与”机制​ 3.2 仲裁机制 REF CAN&#xff08;Controller Area Network&#xff09;总线协议是由 BOSC…

2024年湖北建筑企业申报助理工程师初级职称评审纸质版证书

2024年湖北建筑企业申报助理工程师初级职称评审纸质版证书 湖北省在2022年就发布了一个相关职称实行电子版文件&#xff0c;很多人就理所当然认为工程行业纸质版初级职称/助理工程师、中级职称有些局限。申报评审成功是否可以使用&#xff0c;怎么查询之类的一系列问题&#x…

C++算法入门练习——数据流第K大元素

现有一个初始为空的序列S&#xff0c;对其执行n个操作&#xff0c;每个操作是以下两种操作之一&#xff1a; 往序列S中加入一个正整数x&#xff1b;输出当前序列S​中第k​大的数。 其中&#xff0c;第k大是指将序列从大到小排序后的第k个数。 利用stl里的priority_queue自动…

【工具使用】Keil常用的调试操作整理介绍

目录 一、软件调试使用 1.1 基本调试操作 1.2 调试窗口 二、注意事项 一、软件调试使用 1.1 基本调试操作 上文已经说过在线调试和模拟调试的配置过程&#xff0c;但无论是在线还是模拟调试&#xff0c;调试技巧工具的使用都是一样的。 点击红色d&#xff0c;进入调试 可…

实时天气(预报)API接口

实时天气预报API接口 一、实时天气(预报)API接口二、使用步骤1、接口2、请求参数3、请求参数示例4、接口 返回示例 三、 如何获取appKey和uid1、申请appKey:2、获取appKey和uid 四、重要说明 一、实时天气(预报)API接口 一款帮助你获取实时天气和天气预报的API接口 二、使用步…

量子计算:探索未来的计算技术

量子计算:探索未来的计算技术 引言 在过去的几十年里,我们见证了计算机技术从简单的计算和存储发展到复杂的数据处理和人工智能的飞速进步。然而,随着我们进一步探索科技的前沿,传统的计算方法开始显示出其局限性。在这种情况下,量子计算——一种基于量子力学原理的新型计…

Redis 基础、字符串、哈希、有序集合、集合、列表以及与 Jedis 操作 Redis 和与 Spring 集成。

目录 1. 数据类型 1.1 字符串 1.2 hash 1.3 List 1.4 Set 1.5 sorted set 2. jedis操作redis 3. 与spring集成 1. 数据类型 1.1 字符串 String是最常用的数据格式&#xff0c;普通的kay-value都归结为此类&#xff0c; value值不仅可以是string&#xff0c;可以是数字…

数据库中生成列的对比

简介 生成列&#xff08;虚拟列&#xff09;&#xff1a;在实际开发中&#xff0c;相对一个历史数据的表增加一个字段&#xff0c;增加下游报表&#xff0c;数据分析的可用性。常见的方法就是删表重建&#xff0c;或者使用ADD column 语法。如果是一个历史表&#xff0c;删…