【一天一个计算机知识】—— 【 C/C++ 内存管理与分布】
⚡ CYBER_PROFILE ⚡
/// SYSTEM READY ///
[WARNING]: DETECTING HIGH ENERGY
🌊 🌉 🌊 心手合一 · 水到渠成
| >>> ACCESS TERMINAL <<< | |
| [ 🦾 作者主页 ] | [ 🔥 C语言核心 ] |
| [ 💾 编程百度 ] | [ 📡 代码仓库 ] |
---------------------------------------
Running Process: 100% | Latency: 0ms
索引与导读
🚩一、C/C++ 内存分布
我们先来看下面的一段代码和相关问题:
#include<stdio.h>#include<stdlib.h>// 【全局/静态区】.data:已初始化的全局变量int globalVar =1;// 【全局/静态区】.data:已初始化的静态全局变量(作用域限制在本文件)staticint staticGlobalVar =1;voidTest(){// 【全局/静态区】.data:已初始化的静态局部变量(生命周期为整个程序)staticint staticVar =1;// 【栈区】:局部变量,函数运行结束自动释放int localVar =1;// 【栈区】:局部数组int num1[10]={1,2,3,4};// 【栈区】:字符数组(注意:"abcd" 会被拷贝到栈上)char char2[]="abcd";// 【指针在栈区】,但指向的 "abcd" 字符串字面量在【常量区/代码段】constchar* pChar3 ="abcd";// 【指针在栈区】,指向【堆区】通过 malloc 分配的内存int* ptr1 =(int*)malloc(sizeof(int)*4);// 【指针在栈区】,指向【堆区】通过 calloc 分配并初始化的内存int* ptr2 =(int*)calloc(4,sizeof(int));// 【指针在栈区】,将 ptr2 原有的【堆区】内存进行扩充/移动int* ptr3 =(int*)realloc(ptr2,sizeof(int)*4);// 手动释放【堆区】内存free(ptr1);free(ptr3);}

🚩二、C语言的动态内存管理
🔗Lucy的空间骇客裂缝:C语言动态内存管理
💪C动态内存管理的面试考点
1)realloc的工作机制
voidTest(){int* p2 =(int*)calloc(4,sizeof(int));int* p3 =(int*)realloc(p2,sizeof(int)*10);free(p3 );}- 这里需要
free(p2)吗 - 不需要。 甚至绝对不能这样做
原因在于realloc的工作机制:如果realloc成功扩容,它会返回一个新的内存地址(可能是原地址,也可能是搬迁后的新地址)。如果返回了新地址,原内存块(p2)会自动被释放。如果你手动执行free(p2),会导致“重复释放”(Double Free)的错误,这通常会引起程序崩溃
2)malloc/calloc/realloc的区别是什么?
| 函数 | 功能描述 | 初始化行为 | 参数形式 |
|---|---|---|---|
| malloc | 分配指定字节大小的内存。 | 不初始化。内存里是随机的垃圾值。 | malloc(size_t size) |
| calloc | 分配 n 个元素的内存,并清零。 | 自动初始化为 0。 | calloc(n, size) |
| realloc | 调整已分配内存块的大小。 | 不对新增空间进行初始化。 | realloc(ptr, new_size) |
🚩三、C++ 动态内存管理
C语言内存管理方式在C++中可以继续使用C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理
1)操作内置类型
在C++中,内置类型(如 int, double, char 等)的动态内存分配主要通过 new 和 delete 运算符来实现
1.1)单个变量的分配和释放
- 分配:
new类型 或new类型(初始值) - 释放:
delete指针变量
intmain(){// 1. 分配一个未初始化的 intint* p1 =newint;// 2. 分配并初始化为 100int* p2 =newint(100);// 使用完毕后必须释放内存delete p1;delete p2;// 良好的习惯:将指针置为空,防止野指针 p1 =nullptr; p2 =nullptr;return0;}1.2)一维数组的分配与释放
处理数组时,需要使用方括号 []
- 分配:
new类型[元素个数] - 释放:
delete[]指针变量 (注意:释放数组必须加[],否则会导致未定义行为或内存泄漏)
voidmanageArray(){int size =5;// 分配一个长度为 5 的 int 数组int* arr =newint[size];// 初始化数组(C++11 之后也可以使用 new int[5]{1, 2, 3, 4, 5})for(int i =0; i < size;++i){ arr[i]= i *10;}// 释放数组内存delete[] arr;}2)操作自定义类型
对于自定义类型(如 struct 或 class),C++ 的 new 和 delete 不仅仅是分配内存,它们还负责调用构造函数和析构函数
- 最核心的差异在于:自定义类型的生命周期管理包含了初始化(调用构造函数) 和清理(调用析构函数) 的过程
#include<iostream>usingnamespace std;classA{public:A(int a =0):_a(a){ cout <<"A():"<<this<< endl;}~A(){ cout <<"~A():"<<this<< endl;}private:int _a;};intmain(){ A* p1 =(A*)operatornew(sizeof(A)); A* p2 =newA(1);operatordelete(p1);delete p2;int* p3 =(int*)operatornew(sizeof(int));int* p4 =newint;operatordelete(p3);delete p4; A* p5 =(A*)operatornew(sizeof(A)*10); A* p6 =new A[10];operatordelete[](p5);delete[] p6;return0;}3)operator new与operator delete函数
new和delete是用户进行动态内存申请和释放的操作符,operator new和operator delete是系统提供的全局函数new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间
new表达式 (newexpression): 这是我们在代码中常用的Type* p = new Type();。它是一个“全自动”过程,做了两件事:- 调用
operator new分配原始内存。 - 调用构造函数初始化对象。
- 调用
operator new(函数): 这是一个类似于malloc的函数,它的唯一任务是分配一块指定大小的原始内存,不负责构造对象。
3.1)operator new详解
🔗Lucy的空间骇客裂缝:operator new官方解析
- 原型:
void*operatornew(size_t size);- 工作原理
- 参数:
size是需要分配的字节数(编译器会自动计算对象大小并传入)。 - 返回值: 返回指向分配的内存起始地址的
void*指针。 - 失败处理: 默认情况下,如果分配失败,它不会返回
nullptr,而是抛出std::bad_alloc异常。 - 自定义行为: 你可以重载全局或类特定的
operator new来实现自己的内存池或审计逻辑
- 参数:
3.2)operator delete 详解
🔗Lucy的空间骇客裂缝:operator delete官方解析
- 原型:
voidoperatordelete(void* ptr)noexcept;- 工作原理
- 对应关系:
delete表达式会先调用对象的析构函数,然后调用operator delete来释放内存。 - 安全性:根据 C++ 标准,向
operator delete传递nullptr是安全的,函数会直接返回而不执行任何操作。 - 释放逻辑:在底层,它通常封装了
free()或者系统底层的内存回收接口。
- 对应关系:
在C++中,noexcept是一个异常说明,它告诉编译器和程序员:这个函数保证不会抛出异常
通过上述两个全局函数的实现知道,operator new 实际也是通过 malloc 来申请空间,如果 malloc 申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过 free 来释放空间的
4)new和delete的实现原理
4.1)内置类型
如果申请的是内置类型的空间,new 和 malloc,delete 和 free 基本类似,不同的地方是:new/delete 申请和释放的是单个元素的空间,new[] 和 delete[] 申请的是连续空间,而且 new 在申请空间失败时会抛异常,malloc 会返回 NULL
4.2)自定义类型
new的原理- 调用
operator new函数申请空间 - 在申请的空间上执行构造函数,完成对象的构造
- 调用
delete的原理- 在空间上执行析构函数,完成对象中资源的清理工作
- 调用
operator delete函数释放对象的空间
new T[N]的原理- 调用
operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请 - 在申请的空间上执行
N次构造函数
- 调用
delete[]的原理- 在释放的对象空间上执行
N次析构函数,完成N个对象中资源的清理 - 调用
operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间
- 在释放的对象空间上执行
5)malloc/free和new/delete的区别
malloc/free 和 new/delete 的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:
malloc和free是函数,new和delete是操作符malloc申请的空间不会初始化,new可以初始化malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可malloc的返回值为void*,在使用时必须强转,new不需要,因为new后跟的是空间的类型malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常- 申请自定义类型对象时,
malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理释放
💻结尾— 核心连接协议
警告:🌠🌠正在接入底层技术矩阵。如果你已成功破解学习中的逻辑断层,请执行以下指令序列以同步数据:🌠🌠
【📡】 建立深度链接:关注本终端。在赛博丛林中深耕底层架构,从原始代码到进阶协议,同步见证每一次系统升级。
【⚡】 能量过载分发:执行点赞操作。通过高带宽分发,让优质模组在信息流中高亮显示,赋予知识跨维度的传播力。
【💾】 离线缓存核心:将本页加入收藏。把这些高频实战逻辑存入你的离线存储器,在遭遇系统崩溃或需要离线检索时,实现瞬时读取。
【💬】 协议加密解密:在评论区留下你的散列码。分享你曾遭遇的代码冲突或系统漏洞(那些年踩过的坑),通过交互式编译共同绕过技术陷阱。
【🛰️】 信号频率投票:通过投票发射你的选择。你的每一次点击都在重新定义矩阵的进化方向,决定下一个被全量拆解的技术节点。