C++ 面向對象程序設計–內存分區詳解

一、分區的意義

在講分區前,先談談內存分區的意義,也就是為什麼程序要進行分區?

筆者認為這是為瞭編程的靈活性,因為將內存分區後,不同區域的內存,相關的數據就有的不同的生命周期。以筆者之前的一篇算法復雜度的blog中提到棧幀空間為例,在此就是指棧區,而棧區多指非main函數調用的內存(相關參數等),當非main函數調用結束後,這塊的內存就會清空釋放。而這裡的數據就可以和在main函數中的數據有不同的生命周期。

二、代碼區

1、定義

存放函數體內的二進制代碼,由操作系統進行管理

註意:代碼區是在程序運行前的內存區域,除瞭代碼區,運行前還有全局區。代碼區存放的二進制代碼其實就是編譯器(cpu)執行的指令。

2、特點

1)代碼區是共享的,之所以共享是因為對於被頻繁執行的程序,隻需要在內存中存放一份代碼即可。避免內存過多的浪費。

2)代碼區是隻讀的,原因是防止程序意外地修改瞭它的指令。

三、全局區

前面提到全局區,也在程序執行前。

1、定義

存放全局變量和靜態變量以及常量

2、特點

該區域的數據在程序結束後由操作系統釋放。

3、相關代碼

1)全局變量

#include<iostream>
#include<string>
using namespace std;
// 全局變量
int g_a = 10;
int g_b = 10;
int main()
{
	// 全局區
	// 全局變量、靜態變量、常量
	// 創建普通局部變量
	int a = 10;
	int b = 10;
	cout << "局部變量a的地址為: " << (int)&a << endl
		<< "局部變量b的地址為: " << (int)&b << endl;
	cout << "全局變量g_a的地址為: " << (int)&g_a << endl;
	cout << "全局變量g_b的地址為: " << (int)&g_b << endl;

	system("pause");
	return 0;
}

程序執行結果:

從結果看,局部變量和全局變量存放的地址顯然有很大的不同,而從a和b以及g_a和g_b的地址來看,全局變量和局部變量相鄰的變量的內存地址是相近,從結果看是差瞭4,這是因為單位int占4個字節。

2)靜態變量

// 靜態變量	 在普通變量前面加static,屬於靜態變量
	static int s_a = 10;
	static int s_b = 10;
	cout << "靜態變量s_a的地址為: " << (int)&s_a << endl;
	cout << "靜態變量s_b的地址為: " << (int)&s_b << endl;

將上述代碼加入到前面的main函數中,運行得到如下結果

3)常量(不包含局部常量即const修飾的局部變量)

#include<iostream>
#include<string>
using namespace std;
// 全局變量
int g_a = 10;
int g_b = 10;
// 全局常量
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
		<< "局部變量b的地址為: " << (int)&b << endl;
	cout << "全局變量g_a的地址為: " << (int)&g_a << endl;
	cout << "全局變量g_b的地址為: " << (int)&g_b << endl;
	// 靜態變量	 在普通變量前面加static,屬於靜態變量
	static int s_a = 10;
	static int s_b = 10;
	cout << "靜態變量s_a的地址為: " << (int)&s_a << endl;
	cout << "靜態變量s_b的地址為: " << (int)&s_b << endl;
	// 常量
	// 字符串常量
	cout << "字符串常量的地址為: " << (int)&"hello world" << endl;
	//const修飾的變量
	// const修飾的全局變量、const修飾的局部變量
	cout << "全局常量c_g_a的地址為: " << (int)&c_g_a << endl;
	cout << "全局常量c_g_b的地址為: " << (int)&c_g_b << endl;
	// 局部常量
	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;
}

四、棧區——程序運行後

1、定義

由編譯器自動分配釋放,存放函數的參數值,局部變量等
註意 :不要返回局部變量的地址,棧區開辟的數據由編譯器自動釋放。

2、相關代碼

#include<iostream>
#include<string>
using namespace std;
// 棧區註意事項——不要返回局部變量的地址
// 棧區的數據由編譯器管理開辟和釋放
int* func(int b)	// 形參數據也會放在棧區
{
	b = 100;
	int a = 10;	// 局部變量	 存放在棧區,棧區的數據在函數執行完後自動釋放
	return &a;	// 返回局部變量的地址
}

int main()
{
	// 接收func函數的返回值
	int* p = func(1);
	cout << *p << endl;	// 第一次可以打印正確數字,是因為編譯器做瞭保留
	cout << *p << endl;	// 第二次就不保留瞭

	system("pause");
	return 0;
}

五、堆區——運行後

1、定義

由程序員分配釋放,若程序員不釋放,程序結束時由操作系統回收

c++中主要利用new在堆區開辟內存

2、相關代碼和運行結果

#include<iostream>
#include<string>
using namespace std;
int* func()
{
	// 利用new關鍵字 可以將數據開辟到堆區
	// 指針 本質也是局部變量,放在棧上,指針保存的數據是放在堆區
	int* p = new int(10);
	return p;
}

int main()
{
	// 在堆區開辟數據
	int* p = func();
	cout << *p << endl;
	cout << *p << endl;
	cout << *p << endl;
	cout << *p << endl;
	system("pause");
	return 0;
}

總結

本片文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: