C++内存分区模型(代码区、全局区、栈区、堆区、new & delete)


1 内存分区模型

内存分区的意义:不同区域存放的数据具有不同的生命周期,增加编程的灵活性。

C++内存分区模型包含4个区:
(1)代码区:存储函数体的二进制代码;由操作系统进行管理。
(2)全局区:存储全局变量静态变量常量;在程序结束后,由操作系统管理并释放。
(3)栈区:存储函数的参数值(形参值)局部变量等;由编译器自动分配和释放。
(4)堆区:由程序员自行分配和释放;若未主动释放,在程序结束后,由操作系统管理并释放。


2 程序运行前:代码区 & 全局区

程序编译生成可执行程序(.exe文件),可执行程序未执行前即存在代码区全局区等2个区域。

2.1 代码区

​(1)代码区存储CPU执行的机器指令
(2)代码区是共享的:对于频繁被执行的程序,内存中只需存储一份代码,避免造成内存资源浪费;
(3)代码区是只读的:防止程序意外地修改已存储的指令。


2.2 全局区

(1)全局区存储全局变量静态变量(static修饰的变量)
(2)全局区包含常量区:即字符串常量其它常量(const修饰的全局变量,即全局常量)
(3)全局区的数据在程序结束后,由操作系统管理并释放。

示例

#include <iostream>
using namespace std;

/* 全局变量 */
int g_var = 1;

/* 全局常量:const修饰的全局变量 */
const int c_g_var = 5;

int main() {
    
    
	/* 局部变量 */
	int l_var = 2;

	/* 静态变量:static修饰的变量 */
	static int s_var = 3;

	/* 常量 */
	//字符串常量:"hello"
	
	/* 其他常量:const修饰的变量 */
	//const修饰的局部变量:局部常量
	const int c_l_var = 4;

	cout << "全局变量g_var:" << &g_var << endl;				//0066C000	全局区
	cout << "静态变量s_var:" << &s_var << endl;				//0066C004	全局区
	cout << "常量-字符串常量:" << &"hello" << endl;			//00669B74	全局区
	cout << "常量-全局常量c_g_var:" << &c_g_var << endl;	//00669B30	全局区

	cout << "局部变量l_var :" << &l_var << endl;			//00EFFB34	非全局区
	cout << "局部常量c_l_var:" << &c_l_var << endl;			//00EFFB28	非全局区

	return 0;
}

小结

C++在程序运行前分为全局区代码区
全局区存储:全局变量静态变量(static修饰的变量)常量(①字符串常量;②全局常量,即const修饰的全局变量)。
注:局部变量局部常量(const修饰的局部变量)不在全局区


3 程序运行后:栈区 & 堆区

3.1 栈区

栈区数据编译器自动分配和释放,存储函数的参数值(形参值)局部变量

注:栈区的数据在执行完毕后由编译器自动释放,不能返回局部变量的地址(即不能使用指针访问和操作该内存)。但编译器会保留一次局部变量的地址(可临时访问一次),随后该内存被释放,无法再次访问。

示例

#include <iostream>
using namespace std;

int* func(int param) {
    
    	//函数形参值-栈区
	param = 10;

	int a = 5;			//局部变量-栈区
	/* 不要返回局部变量的地址 */
	return &a;
}

int main() {
    
    
	int* p = func(0);

	//编译器会保留一次局部变量的地址(可临时访问一次),随后该内存被释放,无法再次访问
	cout << *p << endl;		//5		
	cout << *p << endl;		//12062780(乱码)	//内存已被释放

	return 0;
}

3.2 堆区

堆区数据程序员自行分配和释放。若未主动释放,当整个程序全部结束时由操作系统回收。

注1:C++中,使用关键字new在堆区开辟内存空间。
注2:堆区数据在内存未释放前可使用指针多次访问和操作该内存,直至主动释放内存或程序执行完毕后由操作系统释放内存。

示例

#include <iostream>
using namespace std;

//使用关键字new在堆区开辟内存空间
int* func() {
    
    
	int* p = new int(5);
	return p;
}

int main() {
    
    
	int* p = func();

	//堆区数据在内存未释放前可使用指针多次访问或操作,直至主动释放内存或程序执行完毕后由操作系统释放内存
	cout << *p << endl;		//5
	cout << *p << endl;		//5

	*p = 10;
	cout << *p << endl;		//10
	cout << *p << endl;		//10

	return 0;
}

4 new操作符 & delete操作符

C++中,使用new操作符开辟堆区数据,delete操作符释放堆区内存。

注:堆区内存空间由程序员自行分配和释放。若未主动释放,当整个程序全部结束时由操作系统回收。

4.1 堆区变量

开辟内存数据类型 *指针名 = new 数据类型(初始值);
释放内存delete 指针名;

注1:使用new操作符创建堆区变量,返回堆区内存的地址,可使用相同数据类型栈区指针变量接收。
注2:释放内存后,再次访问该内存空间为非法操作,编译器报错:读取访问权限冲突;使用未初始化的内存。

示例1: 堆区变量的创建与释放

#include <iostream>
using namespace std;

//使用关键字new在堆区开辟内存空间
int* func_var() {
    
    
	int* p = new int(5);	//开辟堆区内存
	return p;
}

int main() {
    
    
	int* p = func_var();
	cout << *p << endl;		//5

	/* 无法访问已释放的内存 */
	delete p;				//释放内存
	//cout << *p << endl;	//异常:读取访问权限冲突。使用未初始化的内存"p"

	return 0;
}

4.2 堆区数组

开辟内存数据类型 *指针名 = new 数据类型[数组长度];
释放内存delete[] 指针名;

注:使用new操作符创建堆区数组,返回堆区内存的首地址,可使用相同数据类型栈区指针变量接收。

示例2:堆区数组的创建与释放

//堆区开辟数组
#include <iostream>
using namespace std;

//使用关键字new在堆区开辟数组
int* func_arr() {
    
    
	int* pArr = new int[5];	//开辟堆区数组
	return pArr;
}

int main() {
    
    
	int* arr = func_arr();
	
	//为数组元素赋值
	for (int i = 0; i < 5; i++) {
    
    
		arr[i] = i;		//使用索引操作数组元素
	}

	//遍历数组元素
	for (int i = 0; i < 5; i++) {
    
    
		cout << arr[i] << endl;		//使用索引访问数组元素
	}

	/* 无法访问已释放的内存 */
	delete[] arr;			//释放内存
	//cout << *arr << endl;	//异常:读取访问权限冲突。

	return 0;
}

猜你喜欢

转载自blog.csdn.net/newson92/article/details/112385022