C++程序在执行时,将内存大方向划分为4个区域
- 代码区
存放函数体的二进制代码,由操作系统进行管理 - 全局区
存放全局变量和静态变量以及常量(字符串常量、全局常量) - 栈区
由编译器自动分配释放,存放函数的参数值、局部变量等 - 堆区
由程序员分配和释放,若程序不释放,程序结束时由操作系统回收
内存四区的意义:不同区域存放的数据,赋予不同的生命周期,给我们更灵活的编程创造条件。
1.程序运行前:
在程序编译后,生成了exe可执行程序,未执行该程序前分为2个区域(代码区、全局区)
- 代码区
存放CPU执行的机器指令
代码区是共享的
,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
代码区是只读的
,使其只读的原因是防止程序意外地修改了它的指令 - 全局区
全局变量和静态变量存放在此
全局区还包含了常量区,字符串常量和其他常量也存放在此
该区域的数据在程序结束后由操作系统释放
- 案例演示:
#include <iostream>
using namespace std;
// 全局区:全局变量、静态变量(static 数据类型 变量名)、常量(字符串常量、全局常量(const修饰的全局变量))
// 不在全局区中的:局部变量、const修饰的局部变量(局部常量)
// 全局变量
int global_a = 10;
int global_b = 20;
// 全局常量
const int global_const_a = 10;
const int global_const_b = 20;
int main() {
// 局部变量
int local_a = 10;
int local_b = 20;
cout << "局部变量a的内存地址为:" << (int)&local_a << endl;
cout << "局部变量b的内存地址为:" << (int)&local_b << endl;
cout << "全局变量a的内存地址为:" << (int)&global_a << endl;
cout << "全局变量b的内存地址为:" << (int)&global_b << endl;
// 静态变量
static int a = 10;
static int b = 20;
cout << "静态变量a的内存地址为:" << (int)&a << endl;
cout << "静态变量b的内存地址为:" << (int)&b << endl;
// 常量
// 字符串常量
cout << "字符串常量的内存地址为:" << (int)&"nining" << endl;
// 全局常量
cout << "全局常量a的内存地址为:" << (int)&global_const_a << endl;
cout << "全局常量b的内存地址为:" << (int)&global_const_b << endl;
// 局部常量
const int local_const_a = 10;
const int local_const_b = 20;
cout << "局部常量a的内存地址为:" << (int)&local_const_a << endl;
cout << "局部常量b的内存地址为:" << (int)&local_const_b << endl;
system("pause");
return 0;
}
- 输出结果:
局部变量a的内存地址为:16776620
局部变量b的内存地址为:16776608
全局变量a的内存地址为:10862592
全局变量b的内存地址为:10862596
静态变量a的内存地址为:10862600
静态变量b的内存地址为:10862604
字符串常量的内存地址为:10853380
全局常量a的内存地址为:10853168
全局常量b的内存地址为:10853172
局部常量a的内存地址为:16776596
局部常量b的内存地址为:16776584
请按任意键继续. . .
我们可以看到全局变量、静态变量、字符串常量、全局常量(存放在全局区)的内存地址都很相近,同时局部变量、局部常量(存放在栈区)的内存地址也很相近,存放在全局区的数据内存地址与存放在栈区的数据内存地址具有较大的差别。
2.程序运行后:
- 栈区
由编译器自动分配释放,存放函数的参数值,局部变量等
注意
:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
- 案例演示:
#include <iostream>
using namespace std;
// 创建一个函数,返回局部变量的地址
int* func(int b) //形参数据也放在栈区
{
b = 100;
int a = 10; //局部变量 存放在栈区,栈区的数据在函数执行完后自动释放
return &a;
}
int main() {
int* p = func(1);
cout << *p << endl; //第一次能够正确的打印,是因为编译器对释放的数据进行了一次保留
cout << *p << endl; //第二次出现乱码,也就是不能够正确地打印数据,是因为编译器不再保留数据了
system("pause");
return 0;
}
- 输出结果:
10
16584764
请按任意键继续. . .
- 堆区
由程序员分配释放,若程序员不释放,程序结束时由操作系统回收,在C++中主要利用new关键字在堆区开辟内存
new操作符
:
堆区开辟的数据由程序员手动开辟、手动释放,开辟数据利用操作符new
语法1:数据类型 * p = new 数据类型(初始值)
利用new创建的数据,会返回该数据对应的类型的指针
语法2:数据类型 * arr = new 数据类型[数组长度]
delete操作符
:
堆区开辟的数据由程序员手动开辟、手动释放,释放利用操作符delete
语法1:delete p;
语法2:delete[] arr;
- 案例演示:
#include <iostream>
using namespace std;
/*
new int(10);
delete 变量名;
nem int[10];
delete [] 变量名;
*/
int* func()
{
// 利用new关键字可以将数据开辟到堆区
// 指针,在此本质上也是局部变量,放在栈上,指针保存的数据是放在了堆区
int* p = new int(10);
return p;
}
void func2()
{
// 利用new关键字开辟一个数组到堆区
int* arr = new int[10]; // 10代表数组有10个元素
for (int i = 0; i < 10; i++)
{
arr[i] = i + 100;
}
for (int i = 0; i < 10; i++)
{
cout << arr[i] << endl;
}
// 释放堆区数组
// 释放的时候要加上一个中括号
delete[] arr;
}
int main() {
// 在堆区开辟数据
int* p = func();
cout << *p << endl;
cout << *p << endl;
// 如果想要释放堆区的数据,利用关键字 delete
delete p;
// cout << *p << endl; // 已经将数据释放了,如果再访问将会报错
func2();
system("pause");
return 0;
}
- 输出结果:
10
10
100
101
102
103
104
105
106
107
108
109
请按任意键继续. . .