携手创造,一起成长!这是我参与「日新计划 · 8 月更文应战」的第2天,点击检查活动详情
内存分区模型
C++程序在履行时,将内存大方向划分为5个区域
运转前:
- 代码区:寄存函数体的二进制代码,由操作体系进行办理的
- 大局区(静态区):寄存大局变量和静态变量以及常量
- 常量区:常量存储在这里,不允许修正
运转后:
- 栈区:由编译器主动分配开释, 寄存函数的参数值,部分变量等
- 堆区:由程序员分配和开释,若程序员不开释,程序完毕时由操作体系回收
内存四区意义:
不同区域寄存的数据,赋予不同的生命周期, 给咱们更大的灵敏编程
程序运转前
剖析
在程序编译后,生成了exe可履行程序,未履行该程序前分为两个区域
代码区:
寄存 CPU 履行的机器指令
代码区是同享的,同享的目的是对于频频被履行的程序,只需要在内存中有一份代码即可
代码区是只读的,使其只读的原因是避免程序意外地修正了它的指令
大局区:
大局变量和静态变量寄存在此.
大局区还包含了常量区, 字符串常量和其他常量也寄存在此.
==该区域的数据在程序完毕后由操作体系开释==.
示例
原理:比照不同类型数据的地址区别区域划分。
//大局变量
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地址为: " << (int)&a << endl;//(int)将地址信息转成10进制
cout << "部分变量b地址为: " << (int)&b << endl;
cout << "大局变量g_a地址为: " << (int)&g_a << endl;
cout << "大局变量g_b地址为: " << (int)&g_b << endl;
//静态变量
static int s_a = 10;
static int s_b = 10;
cout << "静态变量s_a地址为: " << (int)&s_a << endl;
cout << "静态变量s_b地址为: " << (int)&s_b << endl;
//常量
//1,字符串常量
cout << "字符串常量地址为: " << (int)&"hello world" << endl;
cout << "字符串常量地址为: " << (int)&"hello world1" << endl;
//2.1const润饰的大局变量:大局常量
cout << "大局常量c_g_a地址为: " << (int)&c_g_a << endl;
cout << "大局常量c_g_b地址为: " << (int)&c_g_b << endl;
//2.2const润饰的部分变量
const int c_l_a = 10;
const int c_l_b = 10;
cout << "部分常量c_l_a地址为: " << (int)&c_l_a << endl;
cout << "部分常量c_l_b地址为: " << (int)&c_l_b << endl;
system("pause");
return 0;
}
打印结果:
部分变量在一个段里;大局变量、静态变量、其他常量在另一个段里。
实例刨析:
部分变量:函数体内(栈区)
大局变量:函数体外
静态变量:函数体内(一般变量前加static)
常量:函数体内
1.字符串常量
2.const润饰的变量
(1)const润饰的大局变量:大局常量
(2)const润饰的部分变量(不在大局区;栈区)
总结
- C++中在程序运转前分为大局区和代码区
- 代码区特色是同享和只读
- 大局区中寄存大局变量、静态变量、常量
- 大局区的常量区中寄存 const润饰的大局常量 和 字符串常量
易混点
区别静态变量(static)与const润饰的部分变量
程序运转后
栈区别析
栈区:
由编译器主动分配开释, 寄存函数的参数值,部分变量等
示例
int * func()
{
int a = 10;//部分变量寄存在栈区,栈区的数据在函数履行完成后主动开释。
return &a;//回来部分变量的地址
}
int main() {
int *p = func();
cout << *p << endl;
cout << *p << endl;
system("pause");
return 0;
}
易错点
不要回来部分变量的地址,栈区拓荒的数据由编译器主动开释,函数运转完毕后函数内的部分变量被开释,将无法使用传回的函数体内的部分变量的地址!
留意:依据编译器不同,编译器有时会保存,可是留意不要这么做!
图片刨析:假定编译器只会保存一次函数体内部分变量的地址,即传出的地址只能调用一次。
如果假定建立,那么*func()的调用将不受次数限制,由于func()每次传回的都是最新的地址,而*p只能调用一次,由于*p经过了部分变量的存储,编译器保存了一次地址后将地址开释之后p地址将失效,无法持续访问。
堆区别析
堆区:
由程序员分配开释,若程序员不开释,程序完毕时由操作体系回收
在C++中主要使用new在堆区拓荒内存
示例
int* func()
{
int* a = new int(10);//使用new关键字将数据拓荒到堆区
return a;//只针的本质是部分变量,放在栈上,指针保存的数据是放在堆区的
}
int main() {
int *p = func();
cout << *p << endl;
cout << *p << endl;
system("pause");
return 0;
}
留意点
int(10)是编译器在栈区暂时虚拟出的一块空间,上图代码int* a表明并给这块内存起名为a,类比与4.2.2结构函数中的匿名目标:Person(10)单独写便是匿名目标(等同于int(10)存于栈上,加上new关键字就存在与堆区了。),特色:当前行完毕之后,立刻析构,即体系立即回收掉匿名目标。
结构函数相关代码比照:
//1、结构函数分类
// 依照参数分类分为 有参和无参结构 无参又称为默许结构函数
// 依照类型分类分为 一般结构和复制结构
class Person {
public:
//无参(默许)结构函数
Person() {
cout << "无参结构函数!" << endl;
}
//有参结构函数
Person(int a) {
age = a;
cout << "有参结构函数!" << endl;
}
//复制结构函数
Person(const Person& p) {
age = p.age;
cout << "复制结构函数!" << endl;
}
//析构函数
~Person() {
cout << "析构函数!" << endl;
}
public:
int age;
};
//2、结构函数的调用
void test01() {
//2.1 括号法(常用)
Person p1;//调用无参结构函数,默许结构函数的调用
Person p2(10);//有参结构函数
Person p3(p2);//复制结构函数
//留意1:调用无参结构函数不能加括号,如果加了编译器认为这是一个函数声明
//Person p2()
//2.2 显式法
Person p2 = Person(10); //相当于给匿名目标Person(10)起个名字叫p2
Person p3 = Person(p2);
//Person(10)单独写便是匿名目标(等同于int(10)存于栈上),特色:当前行完毕之后,立刻析构,即体系立即回收掉匿名目标。
//2.3 隐式转换法(简化的显现法)
Person p4 = 10; // Person p4 = Person(10);
Person p5 = p4; // Person p5 = Person(p4);
//留意2:不能使用 复制结构函数 初始化匿名目标 编译器认为是目标声明
//Person (p5);等同于Person p5;
}
int main() {
test01();
system("pause");
return 0;
}
易错点
new int(10)回来的是地址,需要用指针接纳!
总结:
堆区数据由程序员办理拓荒和开释
堆区数据使用new关键字进行拓荒内存
new操作符
C++中使用==new==操作符在堆区拓荒数据
堆区拓荒的数据,由程序员手动拓荒,手动开释,开释使用操作符 ==delete==
语法: new 数据类型
使用new创建的数据,会回来该数据对应的类型的指针
示例1: 基本语法
int* func()
{
int* a = new int(10);
return a;
}
int main() {
int *p = func();
cout << *p << endl;
cout << *p << endl;
//使用delete开释堆区数据
delete p;
//cout << *p << endl; //报错,开释的空间不可访问
system("pause");
return 0;
}
示例2:拓荒数组
//堆区拓荒数组
int main() {
int* arr = new int[10];
for (int i = 0; i < 10; i++)
{
arr[i] = i + 100;
}
for (int i = 0; i < 10; i++)
{
cout << arr[i] << endl;
}
//开释数组 delete 后加 []
delete[] arr;
system("pause");
return 0;
}
易错点
开释数组要加中括号,如果不加中括号可能只会开释一个数据!
导图
扩展
而C言语的内存模型分为5个区:栈区、堆区、静态区、常量区、代码区。每个区存储的内容如下:
1、栈区:寄存函数的参数值、部分变量等,由编译器主动分配和开释,通常在函数履行完后就开释了,其操作方法类似于数据结构中的栈。栈内存分配运算内置于CPU的指令集,功率很高,可是分配的内存量有限,比如iOS中栈区的巨细是2M。
2、堆区:便是通过new、malloc、realloc分配的内存块,编译器不会担任它们的开释工作,需要用程序区开释。分配方法类似于数据结构中的链表。“内存泄漏”通常说的便是堆区。
3、静态区:大局变量和静态变量的存储是放在一块的,初始化的大局变量和静态变量在一块区域,未初始化的大局变量和未初始化的静态变量在相邻的另一块区域。程序完毕后,由体系开释。
4、常量区:常量存储在这里,不允许修正。
5、代码区:顾名思义,寄存代码