引言

程序运行的本质是对数据的处理,而内存则是程序执行的核心舞台。理解内存的物理与逻辑分区,是掌握程序底层行为、编写高效可靠代码的关键基石。内存并非混沌一片,而是被严格划分为代码区、全局区、栈区和堆区。每个区域拥有独特的生命周期、访问规则和管理机制:代码区存放不变的指令,全局区承载静态数据,栈区高效管理局部变量与调用,堆区则提供灵活的动态内存空间。newdelete是C++直接操控堆内存的核心工具,其正确使用直接影响程序的健壮性。深入剖析这些分区的工作原理及动态内存管理策略(如智能指针、内存池),并掌握内存泄漏检测与优化技巧,是构建高性能、高稳定性系统不可或缺的能力。本内容将系统解析这些底层机制,为高级内存管理实践奠定坚实基础。

最后,如果大家喜欢我的创作风格,请大家多多关注up主,你们的支持就是我创作最大的动力!如果各位观众老爷觉得我哪些地方需要改进,请一定在评论区告诉我,马上改!在此感谢大家了。

各位观众老爷,本文通俗易懂,快速熟悉C++,收藏本文,关注up不迷路,后续将持续分享C++纯干货(请观众老爷放心,绝对又干又通俗易懂)。请多多关注、收藏、评论,评论区等你~~~



文章目录

  • 引言
  • 一、内存分区全景图
    • 1.1 四大内存区域详解
    • 1.2 内存布局示意图
  • 二、程序运行前:代码区与全局区
    • 2.1 代码区深入剖析
    • 2.2 全局区深度探索
  • 三、程序运行后:栈区与堆区
    • 3.1 栈区工作机制详解
    • 3.2 堆区动态管理实战
  • 四、new与delete深度探索
    • 4.1 new的多种用法
    • 4.2 delete的进阶技巧
  • 五、内存管理最佳实践与高级技巧
    • 5.1 智能指针实战
    • 5.2 内存池技术
    • 5.3 内存泄漏检测技术
  • 六、综合应用:高性能内存管理
    • 6.1 自定义分配器
    • 6.2 内存优化策略



正 文

一、内存分区全景图

1.1 四大内存区域详解

C++程序在执行时,将内存划分为4个主要区域

内存区域管理方式存放内容生命周期特点
代码区操作系统函数体的二进制代码程序整个运行期共享、只读、稳定
全局区操作系统全局变量、静态变量、常量程序整个运行期数据持久化、可被所有函数访问
栈区编译器函数参数、局部变量函数执行期间自动管理、空间有限、高效
堆区程序员动态分配的数据显式释放或程序结束空间大、灵活控制、需手动管理

内存分区意义: 不同区域存放的数据具有不同的生命周期,为编程提供了更大的灵活性,使我们能够更有效地管理内存资源。

1.2 内存布局示意图

高地址
┌─────────────┐
│   栈区      │ ← 向下增长
├─────────────┤
│             │
│   堆区      │ ← 向上增长
├─────────────┤
│   全局区    │
│  ┌─────────┐│
│  │ .data   ││ → 已初始化全局/静态变量
│  ├─────────┤│
│  │ .bss    ││ → 未初始化全局/静态变量
│  ├─────────┤│
│  │ 常量区  ││ → 字符串常量、全局常量
│  └─────────┘│
├─────────────┤
│   代码区    │
└─────────────┘
低地址

二、程序运行前:代码区与全局区

2.1 代码区深入剖析

  • 存放内容: CPU执行的机器指令;

  • 主要特性:

    • 共享性: 频繁执行的程序只需在内存中保留一份代码;

    • 只读性: 防止程序意外修改指令;

    • 稳定性: 代码在程序运行期间不会改变;

  • 特性验证示例

#include <iostream>void func1() { std::cout << "Function 1\n"; }
void func2() { std::cout << "Function 2\n"; }int main() {// 验证函数地址(代码区)std::cout << "func1地址: " << (void*)func1 << std::endl;std::cout << "func2地址: " << (void*)func2 << std::endl;// 尝试修改代码区(将导致段错误)// char* p = (char*)func1;// *p = 0xC3; // 尝试写入RET指令return 0;
}

2.2 全局区深度探索

  • 存放内容:

    • 全局变量和静态变量

    • 常量(字符串常量、const修饰的全局常量)

生命周期:程序结束后由操作系统释放

数据特性:该区域的数据在程序整个运行期间都存在

全局区结构验证

#include <iostream>// .data段:已初始化全局变量
int g_data = 100;// .bss段:未初始化全局变量
int g_bss;// 常量区:全局常量
const int g_const = 200;
const char* g_str = "Global String";int main() {// 已初始化静态变量(.data)static int s_data = 300;// 未初始化静态变量(.bss)static int s_bss;std::cout << ".data段变量地址:\n";std::cout << "g_data: " << &g_data << "\ns_data: " << &s_data << "\n\n";std::cout << ".bss段变量地址:\n";std::cout << "g_bss: " << &g_bss << "\ns_bss: " << &s_bss << "\n\n";std::cout << "常量区地址:\n";std::cout << "g_const: " << &g_const << "\n";std::cout << "g_str: " << (void*)g_str << "\n";std::cout << "Literal: " << (void*)"Hello World" << "\n";// 局部变量对比(栈区)int local = 400;const int local_const = 500;std::cout << "\n栈区地址:\n";std::cout << "local: " << &local << "\n";std::cout << "local_const: " << &local_const << "\n";return 0;
}

关键发现

  1. 已初始化全局/静态变量相邻(.data段)
  2. 未初始化全局/静态变量相邻(.bss段)
  3. 常量区地址明显低于栈区地址
  4. 相同字符串常量共享同一内存地址

三、程序运行后:栈区与堆区

3.1 栈区工作机制详解

1. 栈帧结构与函数调用

#include <iostream>void inner(int x) {int a = x * 2;std::cout << "Inner栈帧:\n";std::cout << "  a: " << &a << "\n";
}void outer(int y) {int b = y + 5;std::cout << "Outer栈帧:\n";std::cout << "  b: " << &b << "\n";inner(b);
}int main() {int num = 10;std::cout << "Main栈帧:\n";std::cout << "  num: " << &num << "\n";outer(num);return 0;
}

2. 输出分析

Main栈帧:num: 0x7ffd4d4a5a4c
Outer栈帧:b: 0x7ffd4d4a5a2c
Inner栈帧:a: 0x7ffd4d4a5a0c

地址递减趋势清晰展示了栈的增长方向(高地址→低地址)

3.2 堆区动态管理实战

#include <iostream>int main() {// 单变量动态分配int* pNum = new int(25);std::cout << "堆整数: " << *pNum << " at " << pNum << "\n";// 数组动态分配const int SIZE = 5;double* arr = new double[SIZE]{1.1, 2.2, 3.3, 4.4, 5.5};std::cout << "堆数组: ";for (int i = 0; i < SIZE; ++i) {std::cout << arr[i] << " ";}std::cout << "at " << arr << "\n";// 正确释放内存delete pNum;delete[] arr;// 避免悬空指针pNum = nullptr;arr = nullptr;return 0;
}

四、new与delete深度探索

4.1 new的多种用法

#include <iostream>
#include <new> // 包含bad_alloc和nothrowint main() {// 1. 基本newint* p1 = new int(10);// 2. 数组newint* arr = new int[5]{1, 2, 3, 4, 5};// 3. 异常处理版try {int* big = new int[1000000000000];} catch (const std::bad_alloc& e) {std::cerr << "内存分配失败: " << e.what() << "\n";}// 4. 无异常版(返回nullptr)int* safe = new(std::nothrow) int[1000000000000];if (!safe) {std::cerr << "安全分配失败\n";}// 5. 定位new(在现有内存上构造)char buffer[1024];int* p2 = new (buffer) int(20);std::cout << "定位new值: " << *p2 << " at " << p2 << "\n";// 清理delete p1;delete[] arr;return 0;
}

4.2 delete的进阶技巧

#include <iostream>class Resource {
public:Resource() { std::cout << "资源获取\n"; }~Resource() { std::cout << "资源释放\n"; }
};int main() {// 1. 基本deleteResource* res = new Resource();delete res;// 2. 数组deleteResource* arr = new Resource[3];delete[] arr; // 调用3次析构函数// 3. 虚析构函数的重要性class Base {public:virtual ~Base() { std::cout << "Base析构\n"; }};class Derived : public Base {public:~Derived() override { std::cout << "Derived析构\n"; }};Base* poly = new Derived();delete poly; // 正确调用Derived析构函数// 4. 删除void指针的问题void* pvoid = new int(30);// delete pvoid; // 未定义行为 - 不知道要调用什么析构函数delete static_cast<int*>(pvoid); // 正确方式return 0;
}

五、内存管理最佳实践与高级技巧

5.1 智能指针实战

#include <iostream>
#include <memory>
#include <vector>class Widget {
public:Widget() { std::cout << "Widget创建\n"; }~Widget() { std::cout << "Widget销毁\n"; }void process() { std::cout << "处理Widget\n"; }
};int main() {// 1. unique_ptr(独占所有权)std::unique_ptr<Widget> uptr = std::make_unique<Widget>();uptr->process();// 2. shared_ptr(共享所有权)std::shared_ptr<Widget> sptr1 = std::make_shared<Widget>();{std::shared_ptr<Widget> sptr2 = sptr1;std::cout << "引用计数: " << sptr1.use_count() << "\n";}std::cout << "引用计数: " << sptr1.use_count() << "\n";// 3. weak_ptr(打破循环引用)struct Node {std::shared_ptr<Node> next;std::weak_ptr<Node> prev; // 使用weak_ptr避免循环引用~Node() { std::cout << "节点销毁\n"; }};auto node1 = std::make_shared<Node>();auto node2 = std::make_shared<Node>();node1->next = node2;node2->prev = node1;// 4. 智能指针数组(C++17)auto arr = std::make_unique<int[]>(5);for (int i = 0; i < 5; ++i) {arr[i] = i * 10;}return 0;
}

5.2 内存池技术

#include <iostream>
#include <vector>// 简易内存池实现
class MemoryPool {
public:MemoryPool(size_t blockSize, size_t blockCount): blockSize(blockSize){// 分配大块内存pool = new char[blockSize * blockCount];// 初始化空闲列表for (size_t i = 0; i < blockCount; ++i) {freeList.push_back(pool + i * blockSize);}}~MemoryPool() {delete[] pool;}void* allocate() {if (freeList.empty()) {throw std::bad_alloc();}void* block = freeList.back();freeList.pop_back();return block;}void deallocate(void* block) {freeList.push_back(static_cast<char*>(block));}private:size_t blockSize;char* pool;std::vector<void*> freeList;
};// 使用内存池的类
class PooledObject {
public:static void* operator new(size_t size) {return pool.allocate();}static void operator delete(void* ptr) {pool.deallocate(ptr);}private:static MemoryPool pool;double data[1024]; // 大数据成员
};// 初始化内存池(每个对象8KB,最多100个)
MemoryPool PooledObject::pool(sizeof(PooledObject), 100);int main() {// 使用自定义内存管理PooledObject* obj1 = new PooledObject();PooledObject* obj2 = new PooledObject();delete obj1;delete obj2;return 0;
}

5.3 内存泄漏检测技术

#include <iostream>
#include <cstdlib>// 重载全局new/delete以跟踪分配
void* operator new(size_t size) {void* ptr = malloc(size);std::cout << "分配 " << size << " 字节 at " << ptr << "\n";return ptr;
}void operator delete(void* ptr) noexcept {std::cout << "释放内存 at " << ptr << "\n";free(ptr);
}void operator delete[](void* ptr) noexcept {std::cout << "释放数组 at " << ptr << "\n";free(ptr);
}class Leaky {
public:Leaky() { data = new int[10]; }~Leaky() { } // 故意不删除data
private:int* data;
};int main() {// 正常使用int* p = new int(42);delete p;// 内存泄漏示例Leaky* leaky = new Leaky();delete leaky; // 只删除了对象,内部data泄漏// 数组泄漏double* arr = new double[100];// 忘记delete[]return 0;
}

六、综合应用:高性能内存管理

6.1 自定义分配器

#include <iostream>
#include <vector>
#include <memory>// 栈分配器:从预分配缓冲区分配内存
template <typename T>
class StackAllocator {
public:using value_type = T;StackAllocator(char* buffer, size_t size): buffer(buffer), size(size), offset(0) {}template <typename U>StackAllocator(const StackAllocator<U>& other): buffer(other.buffer), size(other.size), offset(other.offset) {}T* allocate(size_t n) {if (offset + n * sizeof(T) > size) {throw std::bad_alloc();}T* ptr = reinterpret_cast<T*>(buffer + offset);offset += n * sizeof(T);return ptr;}void deallocate(T*, size_t) noexcept {// 栈分配器不实际释放内存}private:char* buffer;size_t size;size_t offset;
};int main() {// 预分配1MB缓冲区const size_t BUFFER_SIZE = 1024 * 1024;char buffer[BUFFER_SIZE];// 使用自定义分配器的vectorusing StackVector = std::vector<int, StackAllocator<int>>;StackAllocator<int> alloc(buffer, BUFFER_SIZE);StackVector vec(alloc);for (int i = 0; i < 1000; ++i) {vec.push_back(i);}std::cout << "使用栈分配器的vector大小: " << vec.size() << "\n";return 0;
}

6.2 内存优化策略

  1. 对象池模式:对频繁创建销毁的对象使用对象池
  2. 小内存分配优化:使用slab分配器管理小对象
  3. 内存对齐:使用alignas确保关键数据结构对齐
    struct alignas(64) CacheLineAligned {int data[16];
    };
    
  4. 写时复制(Copy-on-Write):减少不必要的内存拷贝
  5. 内存映射文件:处理超大文件
    #include <sys/mman.h>
    // 将文件映射到内存
    void* mapped = mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
    


结 束 语

能够看到这里的观众老爷,无疑是对up的最大肯定和支持,在此恳求各位观众老爷能够多多点赞、收藏和关注。在这个合集中,未来将持续给大家分享关于C++的多种常见开发实用操作。未来也将继续分享各种实用干货。感谢大家支持!



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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序71~80

1.总结小程序生命周期 小程序冷启动&#xff0c;钩子函数执行的顺序保留当前页面&#xff0c;进入下一个页面&#xff0c;钩子函数执行的顺序销毁当前页面&#xff0c;进入下一个页面&#xff0c;钩子函数执行的顺序小程序热启动&#xff0c;钩子函数执行的顺序 2.使用Componen…

[Pytest][Part 3]检测python package状态

目录 实现需求1&#xff1a; 检查python package状态——pkg_resource hook实现自动检测包状态 conftest.py hook钩子函数 Part1: https://blog.csdn.net/x1987200567/article/details/144915315?spm1001.2014.3001.5501 从这里开始逐个实现Part1中的需求 实现需求1&a…

自定义时间范围选择组件使用教程(基于 Vue 3 + Element Plus)

&#x1f553; 自定义时间范围选择组件使用教程&#xff08;基于 Vue 3 Element Plus&#xff09;✅ 一个灵活实用的时间范围选择器&#xff0c;支持开始时间、结束时间、快捷时间选项、本地双向绑定、插槽扩展等功能。–&#x1f4d8; 一、功能介绍 该组件基于 Element Plus …

YOLOv8 模型转换 ONNX 后 C# 调用异常:一个参数引发的跨平台适配难题

一、问题背景&#xff1a;从 Python 训练到 C# 部署的跨平台需求 作为一名 C# 开发者&#xff0c;我在完成 YOLOv8 模型训练&#xff08;使用 Ultralytics 官方框架&#xff0c;训练数据为自定义目标检测数据集&#xff0c;输入尺寸 640x640&#xff0c;训练轮次 100 轮&#…

Apache Cloudberry 亮相 2025 IvorySQL 生态大会暨 PostgreSQL 高峰论坛

6 月 27 日至 28 日&#xff0c;IvorySQL 2025 生态大会暨 PostgreSQL 高峰论坛在泉城济南顺利召开。本届大会由 IvorySQL 开源数据库社区主办、瀚高基础软件股份有限公司承办&#xff0c;吸引了来自国内外的数据库技术专家、开发者与开源爱好者齐聚一堂&#xff0c;聚焦数据库…

CMake之CMakeLists.txt语法规则

本文主要参考正点原子的应用开发手册&#xff0c;仅作为本人学习笔记使用。 目录 cmake 的使用方法其实还是非常简单的&#xff0c;重点在于编写 CMakeLists.txt&#xff0c;CMakeLists.txt 的语法规则也简单&#xff0c;并没有 Makefile的语法规则那么复杂难以理解&#xff01…

Mysql专题复习

重点内容&#xff1a;1. Mysql架构&#xff1a;客户端 Server层 存储引擎2. 索引数据结构&#xff1a;B树4. 索引优化&#xff1a;覆盖索引、排序、JOIN、分页&#xff1b; COUNT; 索引下推&#xff1b;单/双路排序5. 数据库事务&#xff1b; 锁&#xff1b;隔离级别&#xff…

CLIP的tokenizer详解

一、bytes_to_unicodedef bytes_to_unicode():"""Returns list of utf-8 byte and a corresponding list of unicode strings.The reversible bpe codes work on unicode strings.This means you need a large # of unicode characters in your vocab if you wa…

【如何判断Linux系统是Ubuntu还是CentOS】

要确定您的操作系统是 Ubuntu 还是 CentOS&#xff0c;可以通过以下方法快速检查&#xff1a; 方法 1&#xff1a;通过终端命令&#xff08;推荐&#xff09; 在终端中执行以下命令之一&#xff1a; 查看 /etc/os-release 文件 cat /etc/os-releaseUbuntu 特征&#xff1a;显示…

RISCV Linux 虚拟内存精讲系列二 -- Linux 入口 head.S

通过 Linux 的构建系统&#xff0c;即 Linux 源代码的根目录下的 Makefile&#xff0c;能够找到 vmlinux 的链接文件&#xff0c;从而能够查看其入口代码 head.S:_start&#xff0c; 如下&#xff1a; Linux 构建系统主Makefile: vmlinux.lds: head.S: 找到该入口后&#xff0c…

springAI学习:Advisors

spring AI Advisors类似于拦截器&#xff0c;会对请求的prompt做出特定的修改和增强&#xff08;比如传入历史沟通记录、搜索信息等等&#xff09;&#xff0c;以达到完善prompt的目的。通过Advisors API&#xff0c;开发人员可以创建更为复杂、可重用、可维护的AI组件。下面介…

MySQL CDC与Kafka整合指南:构建实时数据管道的完整方案

一、引言&#xff1a;现代数据架构的实时化需求 在数字化转型浪潮中&#xff0c;实时数据已成为企业的核心资产。传统批处理ETL&#xff08;每天T1&#xff09;已无法满足以下场景需求&#xff1a; 实时风险监控&#xff08;金融交易&#xff09;即时个性化推荐&#xff08;电商…

MATLAB | 绘图复刻(二十一)| 扇形热图+小提琴图

前段时间在小红书刷到了一个很有特色的热力图&#xff0c;由大佬滚筒洗衣机创作&#xff0c;感觉很有意思&#xff0c;尝试 MATLAB 复刻&#xff1a; 作者使用的是 python 代码&#xff0c;赶快去瞅瞅。 复刻效果 正文部分 0.数据准备 数据需要一个用来画热图的矩阵以及一个…

批量PDF转换工具,一键转换Word Excel

软件介绍 今天为大家推荐一款高效的Office文档批量转换工具&#xff0c;能够快速将Word和Excel文件批量转换为PDF格式。 软件特点 这款名为"五五Excel word批量转PDF"的工具体积小巧&#xff0c;不到2M大小&#xff0c;却能实现强大的批量转换功能&#xff0c…

面试150 基本计算器

思路 利用栈&#xff08;stack&#xff09;来保存进入括号前的计算状态&#xff08;包括当前计算结果和符号&#xff09;&#xff0c;以便在括号结束后正确恢复计算上下文。代码通过遍历字符串&#xff0c;识别数字、加号、减号和括号。遇到数字时构造完整数值&#xff1b;遇到…

源哈希(sh)解析

源哈希&#xff08;Source Hashing&#xff09;是一种负载均衡算法&#xff0c;它根据请求的源 IP 地址&#xff08;或其他标识符&#xff09;生成哈希值&#xff0c;然后根据这个哈希值将请求分配到特定的后端服务实例。这种方法常用于确保来自同一客户端的请求始终被路由到同…

axios的使用以及封装

前言&#xff1a; 在现代前端开发中&#xff0c;网络请求是不可避免的核心功能之一。无论是获取后端数据、提交表单信息&#xff0c;还是与第三方 API 交互&#xff0c;高效且可靠的 HTTP 请求库至关重要。axios 作为一款基于 Promise 的 HTTP 客户端&#xff0c;凭借其简洁的 …

github上部署自己的静态项目

前置知识1、要在github部署项目要提交打包后的静态文件(html,css&#xff0c;js)到仓库里2、我们看下github所提供给我们的部署方式有啥&#xff0c;如下所见&#xff1b;要么是/root文件夹&#xff08;就说仓库里全是打包后的产物&#xff1a;html,css&#xff0c;js要全部放到…

能源管理综合平台——分布式能源项目一站式监控

综合性的能源企业管理面临着项目多、分布散、信息孤岛等问题&#xff0c;分布式的多项目能源在线监控管理平台是一种集成了多个能源项目的数据采集、监控、分析和管理的系统。平台集成GIS能力&#xff0c;能够展示项目的整体分布态势&#xff0c;对不同地点、不同类型的能源项目…

修改阿里云vps为自定义用户登录

win系统上找到控制面板-->用户账户-->更改账户类型点击更改账户类型&#xff0c;此时我们看到vps的默认管理员账户Administrator。为了防止vps被别人使用默认账户Administrator攻击&#xff0c;我们添加一个用户账户&#xff0c;点击添加用户账户。 用户名建议奇葩点&…