舟山市网站建设_网站建设公司_前端工程师_seo优化
2025/12/18 16:24:37 网站建设 项目流程

在 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在释放空间前会调用析构函数完成空间中资源的清理释放

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询