内存分区模型
C++程序在执行时,将内存大方向划分为4个区域
- 代码区:存放函数体的二进制代码,由操作系统进行管理。
- 全局区:存放全局变量和静态变量以及常量。
- 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等。
- 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。
意义:
不同区域存放的数据有着不同的生命周期,使编程更加灵活。
程序运行前
程序运行前划分的两个区域:
在程序编译后,生成可执行exe可执行程序,但是未执行程序前分为两个区域
- 代码区:
存放cpu执行的机器指令。
代码区是共享的,共享的目的是对于频繁执行的程序,只需再内存中有一份代码即可。
代码区是只读的,可以防止程序意外的修改了它的指令。
- 全局区
全局变量和静态变量存放在此。
全局区还包含了常量区,字符串常量和其他常量存放在此。
该区域数据在程序结束后由操作系统释放。
#include <iostream>
using namespace std;
// 定义全局变量
int g_a = 10;
int g_b = 10;
// const修饰的全局变量(全局常量)
const int c_g_a = 10;
const int c_g_b = 10;
int main()
{
// 全局区
// 全局变量、静态变量、常量
// 创建普通的局部变量
int a = 10;
int b = 10;
cout << " 局部变量a的地址 " << (long long)&a << " 局部变量b的地址 " << (long long)&b << endl; // 局部变量a的地址6553116局部变量b的地址6553112
cout << " 全局变量g_a的地址 " << (long long)&g_a << " 全局变量g_b的地址 " << (long long)&g_b << endl; // 全局变量g_a的地址4206608全局变量g_b的地址4206612 可见局部变量和全局变量不在一个区
// 静态变量 在普通变量前加static关键字修饰
static int s_a = 10;
static int s_b = 10;
cout << " 静态变量s_a的地址 " << (long long)&s_a << " 静态变量s_b的地址 " << (long long)&s_b << endl; // 静态变量s_a的地址4206616静态变量s_b的地址4206620
// 常量
// 字符串常量
cout << " 字符串常量的地址 " << (long long)&"hello" << endl; // 字符串常量的地址 4210904
// const修饰的变量 : const修饰的全局变量,const修饰的局部变量
cout << " const修饰的全局变量(全局常量) " << (long long)&c_g_a << " const修饰的全局变量(全局常量) " << (long long)&c_g_b << endl; // const修饰的全局变量 4210688 const修饰的全局变量 4210692
const int c_a = 10;
const int c_b = 10;
// const修饰的局部变量
cout << " const修饰的局部变量 " << (long long)&c_a << " const修饰的局部变量 " << (long long)&c_b << endl; // const修饰的局部变量 6553108 const修饰的局部变量 6553104
}
程序运行后
栈区:
由编译器自动分配释放,存放函数的参数值,局部变量等。
注意:
不要返回局部变量的地址,栈区开辟的数据由编译器自动释放。
堆区:
由程序员分配释放,若程序员不释放,程序结束时由操作系统回收。
在C++中主要利用new在堆区开辟内存
栈区注意示例:不要返回局部变量地址
#include <iostream>
using namespace std;
// 栈区数据的注意事项-> 不要返回局部变量的地址
// 栈区的数据由编译器管理开辟和释放
int* func()
{
// 局部变量
int a = 10; // 存放在栈区 栈区数据会在函数执行完自动释放
return &a; // 不要这么返回
}
int main()
{
int * p = func();
cout << *p << endl; // 10 不同编译器效果不同 我的vscode会直接抛异常 Dev-C++中输出为前面的数值 二次输出为0
cout << *p << endl; // 0
}
#include <iostream>
using namespace std;
// 堆区开辟数据
int *func()
{
// 利用new关键字 将数据开辟到堆区
// 指针本质也是局部变量 在栈空间,但是指针保存的数据在堆区
int *p = new int(10);
return p;
}
int main()
{
int *p = func();
cout << *p << endl; // 10
cout << *p << endl; // 10
return 0;
}
四区说完后,再说一下new这个关键字
C++中利用new操作符在堆区开辟数据
堆区开辟的数据,由程序员手动开辟与释放,释放使用操作符delete
语法:
new 数据类型
利用new创建的数据,会返回该数据对应的类型的指针。
#include <iostream>
using namespace std;
// new 关键字
// 基本语法 new 数据类型
// int *func()
// {
// // 在堆区利用new创建整型数据
// int *p = new int(10);
// return p;
// }
// 堆区开辟数组
void test()
{
int *arr = new int[4];
for (int i = 0; i < 4; i++)
{
arr[i] = i + 1; // 数组赋值 1-5
}
// 打印
for (int i = 0; i < 4; i++)
{
cout << arr[i] << "\t";
} // 1 2 3 4
// 数组释放
delete[] arr;
for (int i = 0; i < 4; i++)
{
cout << arr[i] << "\t";
} // 14893472 0 14886672 0
}
int main()
{
// int *p = func();
// cout << *p << endl; // 10
// cout << *p << endl; // 10
// // 手动释放内存
// delete p;
// cout << *p << endl; // 15090080 已经释放了
test();
return 0;
}
如果是二维数组的话进行内存释放如下:
for (int i = 0; i < 4; ++i) {
delete[] arr[i]; // 释放每一行的内存
}
delete[] arr; // 释放指向行指针的内存