计算机的核心资源有CPU/GPU,内存,磁盘,我们本文讲解的是内存分布
一.内存分布
首先我们用之前的知识先判断各个变量以及常量所处哪个区
cpp
int globalVar = 1; //全局变量 在数据段(已经初始化的读写区)
static int staticGlobalVar = 1; //静态全局变量。数据段
void Test()
{
static int staticVar = 1; //静态局部变量,数据段
int localVar = 1; //局部变量,栈
int num1[10] = { 1, 2, 3, 4 };num1 局部数组,存放到栈
char char2[] = "abcd"; //char2 局部数组,栈 ; abcd本身是字符串常量,在常量区
const char* pChar3 = "abcd"; //pchar3局部指针变量,栈
int* ptr1 = (int*)malloc(sizeof(int) * 4); //ptr1这指针变量在栈 ; 指向的内存在堆
int* ptr2 = (int*)calloc(4, sizeof(int)); //同上
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4); //同上,可能在新块,因为有原地扩容异地扩容
free(ptr1);
free(ptr3);
}
cpp
从低地址到高地址
1.代码段
// 所有可执行代码在这里
int main() {
return 0;
}
void function() {
// 函数体二进制代码
}
2。常量区
const char* string_literal = "Hello World"; // "Hello World"常量字符串
const int const_global = 100; // 常量全局变量在RODATA
#define MAX_SIZE 1000 // 宏定义,预处理替换
3.数据段 -- 已经初始化的全局变量
int global_initialized = 42; // 数据段
static int static_global = 123; // 数据段
char global_str[] = "global"; // 数据段(可修改的全局数组)
4.BSS段 --未初始化的全局变量
int global_uninitialized; // BSS段,默认初始化为0
static int static_uninitialized; // BSS段
int global_array[1000]; // 大数组在BSS段
5.堆区 --动态内存分配 //这里的指针都是局部变量,在栈,分配的内存在堆
void heap_example() {
int* ptr1 = (int*)malloc(100 * sizeof(int)); // 堆区
int* ptr2 = new int[50]; // C++堆区
char* str = (char*)calloc(256, sizeof(char)); // 堆区
free(ptr1);
delete[] ptr2;
free(str);
}
6,栈 --局部变量,函数调用
void stack_example(int param) { // 参数param在栈区
int local_var = 10; // 局部变量在栈区
char local_str[] = "stack"; // 局部数组在栈区
int local_array[100]; // 局部数组在栈区
if (true) {
int block_scope_var = 20; // 块作用域变量在栈区
}
// 函数调用信息在栈区
recursive_function(5);
}

内存区域 | 存储内容 | 生命周期 | 读写权限 |
---|---|---|---|
代码段 | 程序代码 | 程序运行期 | 只读 |
RODATA | 字符串常量、const全局变量 | 程序运行期 | 只读 |
数据段 | 已初始化全局/静态变量 | 程序运行期 | 读写 |
BSS段 | 未初始化全局/静态变量 | 程序运行期 | 读写 |
堆区 | 动态分配内存 | 手动管理 | 读写 |
栈区 | 局部变量、函数参数 | 函数作用域 | 读写 |
二.C++内存管理
C++中引入了构造和析构函数,C的内存管理体系就不再那么适合了,因为处理起来考虑较多,所以C++就有了操作符new和delete进行 动态内存管理
cpp
void text()
{
int* ptr1 = new int;
int* ptr2 = new int[10]; //申请十个int类型的空间
int* ptr3 = new int(4); //初始化这个申请的整型
int* ptr4 = new itn[2]{3}; //初始化申请的两个整型,首个初始化为3,没写的默认0
delete ptr1;
delete[] ptr2;
delete ptr3;
delete[] ptr4;
}
1.new 和 delete和malloc / delete区别最大的是其对自定义类型会自动调用其构造函数和析构函数
2.malloc内存分配失败会判断后返回,new失败会默认抛异常std::bad_alloc,当然也可也模仿malloc对其进行返回nullptr的判断
3.不可混用
三.operator new 和 operator delete函数
作用:new在底层调用operator new全局函数来申请空间。delete在底层通过operator delete全局函数来释放空间
结论:实际上operator就是用malloc来分配内存,成功则返回。否则执行用户提供的空间不足应对措施,如果用户提供该措施 就继续申请,否则就抛异常。operator delete最终是通过free****来释放空间的。
四.new和delete原理
内置类型:
如果申请内置类型内存,则new和malloc,delete和free基本类似,
new/delete是申请释放单个元素空间,new[]和delete[]是申请释放连续空间。
new申请失败会抛异常,malloc返回NULL
自定义类型:
- new 使用operator new申请指定类型的一个空间。后调用构造函数
- delete 先析构,释放资源 。然后用operator delete释放对象空间
- new[ ] 使用operator new[ ]函数申请n个空间,后调用n次构造函数
- delete[ ] 析构n次,释放资源,然后调用operator delete[ ]释放对象空间
- malloc和free是函数,new和delete是操作符
- malloc申请的空间不会初始化,new可以初始化
五.定位new
手动实现new,用上面的底层调用operator new和operator delete机制
cpp
A* p1 = new A(1);
delete p1;
//这两行相当于下面几行
A* p2 = (A*)operator new(sizeof(A)); //p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行
new(p2)A(1);
//构造函数不能直接调用
//析构函数可以直接调用
p2->~A();
operator delete(p2);
这个会利用于池化技术。就是等于说可以声明一个获取内存的池子,要是不这样分布实现,直接调用new就会直接从系统堆上申请,效率不高,然后这样的话就可以手动指定获取内存的地方
具体效率高在哪,我们后期linux系统编程的线程池等等会介绍