当前位置: 首页 > news >正文

C/C++内存管理

在 C/C++ 编程中,内存管理是一个核心知识点,直接影响程序的性能和稳定性。本文将详细介绍 C/C++ 中的内存分布、动态内存管理方式及相关原理,帮助大家系统掌握这部分知识。

一、C/C++ 内存分布

程序运行时,内存主要分为以下几个区域:

  1. 栈(堆栈):存储非静态局部变量、函数参数、返回值等,栈是向下增长的。
  2. 内存映射段:高效的 I/O 映射方式,用于装载共享动态内存库,也可用于进程间通信(了解即可)。
  3. :用于程序运行时动态内存分配,堆是向上增长的。
  4. 数据段:存储全局数据和静态数据。
  5. 代码段:存储可执行的代码和只读常量。

内存分布示例分析:

  • char char2[] = "abcd"char2数组在栈上,"abcd" 作为字符串常量在代码段,初始化时会将字符串内容拷贝到栈上的数组中。
  • char* pChar3 = "abcd":指针pChar3在栈上,其指向的字符串常量 "abcd" 在代码段。
  • int* ptr1 = new int[10]:指针ptr1在栈上,其指向的动态内存空间在堆上。

二、C 语言动态内存管理方式

C 语言通过malloccallocreallocfree四个函数进行动态内存管理,它们的区别和特性如下:

函数功能描述初始化特性使用方法
malloc从堆区开辟指定字节数的连续空间不初始化,空间内是随机值void* malloc (size_t size);
calloc从堆区开辟 n 个 “指定大小” 的连续空间(可看作malloc+初始化自动将空间全部初始化为 0void* calloc (size_t num, size_t size);
realloc调整已开辟的堆空间大小(支持扩容 / 缩容扩容时保留原数据,新空间未初始化;缩容时截断数据void* realloc (void* ptr, size_t size);

注意事项:

使用realloc调整空间大小时,若扩容成功,原指针p会被新指针接管,无需单独free(p),只需释放新指针即可。例如:

void Test() { int* p2 = (int*)calloc(4, sizeof(int)); int* p3 = (int*)realloc(p2, sizeof(int) * 10); // 无需free(p2),只需释放p3 free(p3); }

三、C++ 内存管理方式

C 语言的内存管理方式在 C++ 中仍可使用,但 C++ 新增了newdelete操作符,更适合处理自定义类型,使用更简洁。

1. 操作内置类型

  • 申请单个元素空间:new+ 类型,释放用delete
  • 申请连续空间:new[]+ 类型,释放用delete[](必须匹配使用)。
  • 支持初始化,未指定的元素默认置 0。

示例代码:

void Test() { // 动态申请int空间,不初始化 int* p1 = new int; // 动态申请int空间,初始化为10 int* p2 = new int(10); // 动态申请3个int的连续空间,不初始化 int* p3 = new int[3]; // 动态申请10个int的连续空间,全部初始化为0 int* p4 = new int[10] {0}; // 前5个初始化,后5个默认置0 int* p5 = new int[10] {1, 2, 3, 4, 5}; // 释放空间(必须匹配) delete p1; delete p2; delete[] p3; delete[] p4; delete[] p5; }

2. 操作自定义类型

new/deletemalloc/free的核心区别:对自定义类型,new会调用构造函数初始化,delete会调用析构函数清理资源

示例代码:

class A { public: A(int a1 = 0, int a2 = 0) : _a1(a1), _a2(a2) { cout << "A():" << endl; } ~A() { cout << "~A():" << endl; } private: int _a1, _a2; }; int main() { A* p1 = new A(1); // 调用构造函数 A* p2 = new A[3]{A(1,1), A(2,2), A(3,3)}; // 调用3次构造函数 delete p1; // 调用析构函数 delete[] p2; // 调用3次析构函数 return 0; }

四、operator new 与 operator delete 函数

  • newdelete是操作符,底层通过全局函数operator newoperator delete实现内存申请与释放。
  • operator new:内部通过malloc申请空间,失败时抛异常(而非返回 NULL)。
  • operator delete:内部通过free释放空间。

五、new 和 delete 的实现原理

1. 内置类型

  • malloc/free功能类似,但new失败时抛异常,malloc返回 NULL;new[]/delete[]用于连续空间,需匹配使用

2. 自定义类型

  • new的原理

    1. 调用operator new申请空间;

    2. 在空间上执行构造函数。

  • delete的原理

    1. 在空间上执行析构函数;

    2. 调用operator delete释放空间。

  • new T[N]的原理

    1. 调用operator new[]申请 N 个对象的空间;

    2. 执行 N 次构造函数。

  • delete[]的原理

    1. 执行 N 次析构函数;

    2. 调用operator delete[]释放空间。

注意对有析构函数的自定义类型数组,delete必须配合[]使用,否则会因释放位置错误导致崩溃。

示例代码:

class A { public: A(int a1 = 0, int a2 = 0) : _a1(a1) , _a2(a2) { cout << "A():" << endl; } A(const A& aa) : _a1(aa._a1) { cout << "A(const A& aa)" << endl; } ~A() { cout << "~A():" << endl; } private: int _a1 = 0; int _a2 = 0; }; class B { private: int _b1 = 0; int _b2 = 0; }; int main() { //int* p1 = new int[10]; //free(p1); //内置类型可以这样写,不涉及析构 //B中没有显示写析构函数 //B* p2 = new B[10]; //申请10 * 8个字节 //delete p2; //A中显示写析构函数了 A* p3 = new A[10]; //申请8 + 10 * 8个字节,前8个字节放的是个数(64位系统),32位就是4+10 * 8个字节 //delete p3; //报错,因为只释放了一部分,一整段空间不能在中间释放 delete[] p3; // 有析构函数的类释放时要在delete后加上[],实际释放的位置要往前偏移8个字节(64位)或4个字节(32位) return 0; }

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

在已分配的原始内存中手动调用构造函数初始化对象,格式:new (place_address) type(initializer-list)

场景:配合内存池使用(内存池分配的空间未初始化,需手动调用构造函数)。

示例:

A* p = (A*)malloc(sizeof(A)); // 仅开辟空间,未初始化 new (p) A(1, 2); // 调用构造函数初始化 p->~A(); // 手动调用析构函数 free(p); // 释放空间

七、malloc/free 与 new/delete 的区别

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。
不同的地方是:
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在释放空间前会调用析构函数完成空间中资源的清理释放

http://www.jsqmd.com/news/110796/

相关文章:

  • MT3音乐转录:用AI技术让音频秒变乐谱的完整指南
  • Next.js零基础入门:第一个项目全指南
  • AI如何实现代码无限续杯?快马平台实战解析
  • PathOfBuilding使用指南:从Build规划新手到专家的系统性转变
  • Kotaemon专利检索工具:连接WIPO数据库
  • AI应用架构师干货:虚拟工作系统的AI能力评估指标与架构优化方法论
  • Kotaemon模型量化技巧:INT8/FP16压缩部署
  • 传统抢票VS脚本抢票:效率对比实测分析
  • ADB工具箱终极指南:告别命令行,拥抱高效设备管理新时代
  • Luckysheet数据验证终极指南:告别数据录入错误的完整教程
  • Kotaemon溯源功能详解:每条答案都能追根溯源
  • 智能电费管家:南方电网数据接入Home Assistant全攻略
  • 零基础教程:用Docker部署你的第一个Dify应用
  • Adobe Illustrator自动化脚本实战指南:从新手到专家的效率革命
  • C++中引用类型变量 int
  • 节省3小时!快速解决dpkg依赖问题的5个技巧
  • 传统调试vsAI解决:图形显示错误处理效率对比
  • 使用qtscrcpy在电脑上进行安卓手机编程
  • 对时间序列进行分类
  • Kotaemon书法教学助手:笔画顺序与名家赏析
  • CellProfiler生物图像分析:从入门到精通的完整指南
  • Python 一键抠图神器:remove-bg 终极使用指南
  • Django QuerySet filter 完全指南:高效查询数据的核心技巧
  • Vue插槽vs传统组件:开发效率对比实验
  • 别再为选BI工具头疼了!2026年这5款主流平台,我们帮你从核心到场景做了全拆解。
  • 点财网网站介绍 - 资讯焦点
  • 2025年耐火桥架生产厂家权威推荐榜单:防腐桥架/桥架/不锈钢桥架源头厂家精选 - 品牌推荐官
  • Kotaemon缓存命中率优化:减少LLM调用次数
  • 零基础入门:5分钟学会使用Deformable DETR做目标检测
  • 开源无人机影像处理利器ODM:从航拍图片到三维模型的完整解决方案