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

C++动态内存/内存管理

文章目录

前言

一、内存分区

二、C 语言动态内存(标准库函数)

1.核心函数

2.代码示例

3.关键注意点

三、C++ 动态内存(关键字 / 操作符)

1.核心用法

(1)单个对象

(2)数组对象

(3)类对象

2.实现原理

(1)内置类型

(2)自定义类型

3.关键特性

四、区别

栈区 vs 堆区

C/C++ 动态内存--核心区别

五、常见错误


前言

动态内存是程序手动在堆区申请、使用、释放的内存,区别于栈区内存的自动管理,是 C/C++ 核心考点和开发必备知识点。

整体分为C 语言动态内存C++ 动态内存两大体系,核心围绕内存分配、释放、常见错误、最佳实践展开。


一、内存分区

程序运行时内存分为 4 个区域,动态内存仅存在于堆区

  1. 栈区:局部变量、函数参数,系统自动分配 / 释放,空间小、连续、速度快。

  2. 堆区:动态内存,手动申请 / 释放,空间大、不连续、速度慢,易产生内存碎片。

  3. 全局 / 静态区:全局变量、static变量,程序结束后系统释放。

  4. 常量区:字符串常量、const 常量,只读不可修改。

二、C 语言动态内存(标准库函数)

依赖<stdlib.h>头文件

核心 4 个函数:malloc/calloc/realloc/free成对使用,手动管理

1.核心函数

函数

语法

功能

特点

malloc

void* malloc(size_t size)

申请指定字节数的堆内存

不初始化内存,返回void*,失败返回NULL

calloc

void* calloc(size_t num, size_t size)

申请numsize大小的内存

自动初始化为 0,参数拆分更直观

realloc

void* realloc(void* ptr, size_t new_size)

调整已分配内存的大小

原地扩容 / 异地扩容;ptr=NULL等价malloc

free

void free(void* ptr)

释放堆内存

无返回值;仅能释放堆内存free(NULL)安全

2.代码示例

#include <stdio.h> #include <stdlib.h> int main() { // 1. malloc申请4字节int内存,强转类型 int* p1 = (int*)malloc(sizeof(int)); if (p1 == NULL) { // 必须判断!防止分配失败 perror("malloc fail"); return 1; } *p1 = 10; // 2. calloc申请5个int,初始化为0 int* p2 = (int*)calloc(5, sizeof(int)); // 3. realloc扩容为10个int int* p3 = (int*)realloc(p2, 10 * sizeof(int)); if (p3 != NULL) p2 = p3; // 扩容成功,更新指针 // 4. 释放内存,后置空(避免野指针) free(p1); free(p2); p1 = p2 = NULL; return 0; }

3.关键注意点

  • 必须强转类型malloc返回void*,需转为目标指针类型;

  • 必须判断返回值:分配失败返回NULL,直接使用会崩溃;

  • free仅释放堆内存,不能释放栈指针;释放后指针必须置NULL

三、C++ 动态内存(关键字 / 操作符)

C++ 兼容 C 的malloc/free,但推荐专用的new/deletenew[]/delete[],依赖 C++ 语法特性,更安全、更面向对象。

1.核心用法

(1)单个对象

// 申请int内存,无初始化 int* p1 = new int; // 申请+初始化(推荐) int* p2 = new int(10); // 释放单个对象 delete p1; delete p2;

(2)数组对象

// 申请5个int的数组,不初始化 int* arr = new int[5]; // 释放数组,必须用delete[]! delete[] arr;

(3)类对象

new自动调用构造函数delete自动调用析构函数malloc/free无此功能)

class Test { public: Test() { cout << "构造" << endl; } ~Test() { cout << "析构" << endl; } }; Test* t = new Test; // 调用构造 delete t; // 调用析构

2.实现原理

(1)内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,

不同的地方是:new/delete 申请和释放的是单个元素的空间

new[] 和delete[] 申请的是连续空间,而且new 在申请空间失败时会抛异常,malloc会返回NULL。

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

3.关键特性

  • 类型安全:无需强转,自动返回目标类型指针

  • 自动计算大小:无需手动写sizeof

  • 失败处理:分配失败抛出bad_alloc异常(而非返回NULL);

  • 严格配对newdeletenew[]delete[]不可混用

  • operator new/delete:new 底层调用 operator new,后者通过 malloc 申请空间,失败时抛异(而非返回 NULL);delete 底层调用 operator delete,最终通过 free 释放空间。

  • 定位 new:在已分配的原始内存中显式调用构造函数,用于内存池场景(内存池分配的内存未初始化,自定义类型需手动调用构造)。

四、区别

栈区 vs 堆区

特性栈区 (Stack)堆区 (Heap)
增长方向向下(高地址→低地址)向上(低地址→高地址)
管理方式系统自动分配 / 释放程序员手动分配 / 释放
空间大小小(固定上限)大(动态扩展)
分配速度极快较慢
内存碎片
分配连续性连续内存不连续内存
生命周期函数作用域内手动控制全程

C/C++ 动态内存--核心区别

对比项

malloc / free

new / delete

本质

C 标准库函数

C++关键字 / 操作符

类型安全

返回void*,必须强转

返回目标类型指针,无需强转

内存初始化

不初始化

可直接初始化

内存大小

手动指定字节数

自动计算

失败处理

返回NULL

抛出异常

类对象

不调用构造 / 析构

自动调用构造 / 析构

数组

无专用语法

new[]/delete[]严格配对

五、常见错误

动态内存的 bug 是程序崩溃、内存泄漏的主要原因,核心错误:

  1. 野指针:指针指向已释放 / 非法内存(未初始化、free后未置空、越界);

  2. 内存泄漏:申请的内存未释放,堆内存耗尽(忘记free/delete、指针丢失);

  3. 重复释放:多次free/delete同一个指针,直接崩溃;

  4. 不匹配释放new[]deletenewdelete[](类对象会漏调用析构);

  5. 混用函数malloc搭配deletenew搭配free(未定义行为);

  6. 释放栈内存:用free释放栈区指针(非法操作);

  7. 越界访问:申请 n 个元素,访问 n+1 位置,破坏内存;

  8. realloc后直接使用原指针:扩容失败返回NULL,原指针失效;

  9. 不判断分配结果:直接使用NULL指针;

  10. 内存交叉释放:释放不属于自己的堆内存。

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

相关文章:

  • 破解技术垄断,开源方案拯救[设备类型]
  • **光计算驱动下的编程新范式:用Python实现光子神经网络模拟**在传统电子计算逐渐逼近物理极限的今天,**光计算(Optica
  • OpenClaw多模型切换:GLM-4.7-Flash与其他模型协同工作
  • ROS机械臂避障实战:用MoveIt!和Rviz实现复杂环境下的轨迹规划(附完整配置流程)
  • Polars 2.0快速接入全链路拆解(含Benchmark实测:比Pandas快42.6×,比Dask低68%内存)
  • StarRocks实战:利用UNNEST函数高效解析JSON数组字段
  • STM32远程升级系统设计与实现
  • 告别Postman!用CURL玩转API测试的7个高阶技巧
  • 基于SpringBoot+Vue的新闻管理系统设计与实现+指导搭建视频
  • UniApp自定义导航栏避坑大全:从胶囊适配到主题切换,我踩过的坑你别再踩
  • 告别手动Debug!用Cursor的Playwright MCP插件,自动抓取并修复前端控制台错误
  • GHelper轻量级解决方案:华硕笔记本性能调校完全指南
  • Cadence OrCAD导出PDF标签丢失?3种打印机实测对比与解决方案
  • 深入Tiptap插件开发:从字体样式到行高的自定义实现
  • 手把手教你点亮480x480圆形屏:ST7701s双通道MIPI初始化代码详解与调试心得
  • 全自动内容创作:OpenClaw+Qwen3-32B从选题到发布
  • 嵌入式按键事件处理框架:高可靠消抖与复合操作状态机
  • 逆向进阶(四) CE自动汇编实战:从CT表到独立EXE修改器的完整流程
  • 基于Vue3+Django的图书智能推荐系统设计与实现+文档(协同过滤算法)
  • 怎么安装OpenClaw?2026年京东云萌新6分钟部署保姆级教程
  • 3步解锁游戏扩展能力:面向玩家的插件框架应用指南
  • 如何使用 Dockerfile 创建自定义镜像?
  • 3个维度突破股票数据获取难题:MOOTDX量化分析实战指南
  • 【紧急通知】Python 3.14 JIT默认profile已触发AWS Lambda冷启动恶化阈值!立即执行这4项低成本开关校准
  • 从‘发动鸡’到‘三元催化’:手把手解决中文NER中的口语化与OOV难题(含代码示例)
  • 3款电脑实用神器合集,视频无损分割不压缩、视障友好屏幕阅读器、图片批量一键加水印,日常办公剪辑修图全搞定
  • Zemax新手避坑指南:从零开始搞定一个F/4的单透镜设计(附完整操作截图)
  • OpenClaw多模型切换指南:百川2-13B与Qwen3-32B的自动化任务对比
  • 高效Switch游戏安装:Awoo Installer多源部署技术深度解析
  • 隐式建模的革新:GemPy如何重新定义三维地质结构可视化