2021c++核心编程之内存分区模型(一)

1 内存分区模型

C++程序在执行时,将内存大方向划分为4个区域

  • 代码区:存放函数体的二进制代码,由操作系统进行管理的
  • 全局区:存放全局变量和静态变量以及常量
  • 栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等
  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

内存四区意义:

不同区域存放的数据,赋予不同的生命周期, 给我们更大的灵活编程

1.1 程序运行前

在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域

i.代码区

作用: 存放CPU执行的机器指令

特点:

  • 代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
  • 代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令
ii.全局区

作用: 存放全局变量静态变量以及常量 (包含字符串常量const修饰的全局变量)

特点: 该区域的数据在程序结束后由操作系统释放

示例:

#include<iostream>
using namespace std;
//定义两个全局变量g_a,g_b
int g_a = 10;
int g_b = 10;

//定义两个const修饰的全局变量
const int c_g_a = 10;
const int c_g_b = 10;

//定义两个宏常量
#define  d_g_a  10
#define  d_g_b  10

int main() {

	//定义两个局部变量a,b
	int a = 10;
	int b = 10;

	//定义两个静态变量s_a,s_b
	//注意:局部变量被static修饰之后,属于静态变量
	static int s_a = 10;
	static int s_b = 10;

	//定义两个const修饰的局部变量
	const int c_l_a = 10;
	const int c_l_b = 10;

	//通过地址可以判断出局部变量、全局变量、静态变量以及常量()所在区域
	cout << "局部变量a的地址为:" << &a << endl;
	cout << "局部变量b的地址为:" << &a << endl;
	cout << endl;

	cout << "全局变量g_a的地址为:" << &g_a << endl;
	cout << "全局变量g_b的地址为:" << &g_b << endl;
	cout << endl;
	cout << "静态变量s_a的地址为:" << &s_a << endl;
	cout << "静态变量s_b的地址为:" << &s_b << endl;
	cout << endl;
	//常量目前接触到的有三种,字符串常量和const修饰的变量以及宏常量
	//字符串常量
	cout << "字符串常量的地址为:" << &"hello world" << endl;
	cout << "字符串常量的地址为:" << &"10" << endl;
	cout << endl;
	//const修饰的变量分为:const修饰的全局变量和const修饰的局部变量
	//1.const修饰的全局变量
	cout << "const修饰的全局变量c_g_a的地址为:" << &c_g_a << endl;
	cout << "const修饰的全局变量c_g_b的地址为:" << &c_g_b << endl;
	cout << endl;
	//2.const修饰的局部变量
	cout << "const修饰的全局变量c_l_a的地址为:" << &c_l_a << endl;
	cout << "const修饰的全局变量c_l_b的地址为:" << &c_l_b << endl;
	cout << endl;
	//3.宏常量
	//注意:define的宏变量在使用时是替换不占内存,因此不可寻址
	cout << "宏常量d_l_a的地址为:" << d_g_a << endl;
	cout << "宏常量d_l_b的地址为:" << d_g_b << endl;

	system("pause");
	return 0;
}

打印结果:

在这里插入图片描述

总结:

  • C++中在程序运行前分为全局区和代码区
  • 代码区特点是共享和只读
  • 全局区中存放全局变量、静态变量、常量
  • 常量区中存放 const修饰的全局常量 和 字符串常量

1.2 程序运行后

iii.栈区

作用: 存放局部变量以及函数中定义的形参变量等,但栈区存储数据空间由编译器管理开辟和自动释放

注意事项: 不要返回局部变量的地址,栈区存储数据空间由编译器管理开辟和自动释放

示例:

#include<iostream>
using namespace std;
//注意:形参变量b和函数中定义的局部变量a中的值都存放在栈区,当函数执行完之后,存放在栈区的数据由编译器自动释放
//定义一个函数function1,返回形参变量的地址
int* function1(int b) {
	return &b;//返回形参变量的地址
}
//定义一个函数function1,返回局部变量的地址
int* function2(int b) {
	int a = b;
	return &a;//返回形参变量的地址
}
int main() {
	//调用指针函数function1,并接收返回的形参变量的地址
	int * p1 = function1(10);
	//注意:指针函数function1中的形参变量b中的值在函数调用完之后就自动释放了,导致返回形参的地址指向的值为未知,所以解引用得不到值
	cout << "指针p1的地址为:" << p1 << "\t" << "指针p1中的值为:" << *p1 << endl;
	cout << "指针p1的地址为:" << p1 << "\t" << "指针p1中的值为:" << *p1 << endl;
	//调用指针函数function2,并接收返回的形参变量的地址
	int* p2 = function2(10);
	//注意:函数function2中定义的局部变量a中的值在在函数调用完之后就自动释放了,导致返回局部变量a的地址指向的值为未知,但是编译器会做一次值保留,当再次进行输出时,值不在保留
	cout << "指针p2的地址为:" << p2 << "\t" << "指针p2中的值为:" << *p2 << endl;
	cout << "指针p2的地址为:" << p2 << "\t" << "指针p2中的值为:" << *p2 << endl;

	system("pause");
	return 0;
}

打印结果:

在这里插入图片描述

iv.堆区

作用: 主要利用new在堆区开辟内存

特点: 由程序员分配释放,若程序员不释放,程序结束时由操作系统回收

示例:

#include<iostream>
using namespace std;
//定义一个function函数返回指针指向的地址
int* function() {
	//利用new关键字,可以在堆区内开辟一个存储数据的空间,同时将指向堆区的地址保存在指针中,但指针本质也是局部变量,存放在栈区中,其指向的值在堆区中
	int* p= new int(10);
	return p;
}

int main() {
	//调用function函数时,在堆区中开辟空间用来保存数据,并将数据在堆区的地址返回,这个地址已经指向堆区中存储的值,所以在函数function被调用完之后,不会因为栈区的自动释放而影响堆区通过这个地址解引用获取值
	int* point = function();
	cout << "数据在堆区的地址为:" << point << "数据在堆区的值为" << *point << endl;
	cout << "数据在堆区的地址为:" << point << "数据在堆区的值为" << *point << endl;
	cout << "数据在堆区的地址为:" << point << "数据在堆区的值为" << *point << endl;
	system("pause");
	return 0;
}

打印结果:

在这里插入图片描述

总结:

  • 堆区数据利用new关键字进行开辟内存
  • 堆区数据由程序员管理开辟和释放

1.3 new操作符

作用: 由程序员手动利用new操作符在堆区开辟存储数据空间,手动释放堆区数据利用操作符 delete,若程序员不释放,程序结束时由操作系统回收

语法规则:

new 数据类型;

利用new创建的数据,会返回该数据对应的类型的指针

示例1: 基本语法

#include<iostream>
using namespace std;
//定义一个function函数返回指针指向的地址
int* function() {
	//利用new关键字,可以在堆区内开辟一个存储数据的空间,同时将指向堆区的地址保存在指针中,但指针本质也是局部变量,存放在栈区中,其指向的值在堆区中
	int* p= new int(10);
	return p;
}

int main() {
	//调用function函数时,在堆区中开辟空间用来保存数据,并将数据在堆区的地址返回
	int* point = function();
	cout << "数据在堆区的地址为:" << point << "数据在堆区的值为" << *point << endl;
	cout << "数据在堆区的地址为:" << point << "数据在堆区的值为" << *point << endl;
	cout << "数据在堆区的地址为:" << point << "数据在堆区的值为" << *point << endl;
    //在堆区中存储的数据,由程序员管理开辟和释放,对于开辟使用new关键字,对于释放使用delete关键字
	delete point;
	cout << "数据在堆区的地址为:" << point << "数据在堆区的值为" << *point << endl;//堆区中存储的数据已经被释放,再次访问就变成非法操作,直接报错!!!
	system("pause");
	return 0;
}

效果如图:

在这里插入图片描述

示例2:开辟数组

#include<iostream>
using namespace std;
#define max_arrys 10
//创建一个function3函数返回一个指针类型
int * function3() {
	//在堆区创建一个整型数据的数组,并设置了开辟的空间存储数据容量为10
	int* arrys = new int[max_arrys];
	//添加随机种子,作用是利用当前系统时间生成随机数,防止每次随机都是一样,如果报错,注意:头文件引入 #include <ctime>
	srand((unsigned int)time(NULL));
	//对数组进行赋值
	for (int i = 0; i < max_arrys;i++) {
		arrys[i] = i + rand()%90;
	}
	return arrys;
	
}
int main() {
	//调用function3函数,在堆区中开辟空间用来保存数组数据,并将数组在堆区的地址返回
	int* p  = function3();
	//将数组中的数据输出
	cout << "数组arrys:[";
	for (int i = 0; i < max_arrys; i++)
	{
		if(max_arrys - 1 > i) {
			cout << p[i] << ", ";
		}
		else
		{
			cout << p[i] << "]" << endl;
		}
	}
	//在堆区中存储的数组数据,由程序员管理开辟和释放,对于开辟使用new关键字,对于释放使用delete关键字
	//注意:释放堆区中的数组,必须在delete后面加[]
	delete[] p;
	system("pause");
	return 0;
}

读万卷书,行万里路,博主会持续更新学习笔记,如有问题,可以留言和私信,我们一起进步!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QZP51ZX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值