天天看點

2020/02/25 程式筆記

記憶體相關知識(stack, heap, new, delete)

  • stack

    ,是系統自行管理的

    靜態記憶體空間

    ,用來專門存放區域變數,並會區域變數的生命徵期結束時自行釋放該區域變數。
  • Heap

    ,是我們所自行管理的

    動態記憶體空間

    ,我們使用new動態配置的空間都存放在這邊,需要我們自行手動delete釋放用不到的記憶體空間。
  • 若再使

    new

    動態配置記憶體時,是存在記憶體的

    heap

    ,並且在變數使用完之後記得要

    delete

    ,釋放出記憶體,若沒有刪除動態配置的記憶體,則會造成該記憶體空間能然被占用,且造成記憶體空間破碎,而無法使往後在動態配置時,可以完整的配置剩餘空間,例如:

    假設記憶體為4MB,而我們new了一個動態記憶體變數,系統自動在記憶體中間(系統自己判斷位置)使用了一塊100kb的記憶體,使用完後我們並沒有delete釋放,導緻之後於法配置出完整的3.9MB(4MB-100KB),隻剩下各半的大小,因為未試放的使用完的記體,導緻記憶體破碎成兩半或者更多,是以不能完整的使用剩下的記憶體空間)

  • 在函數內想要宣告區域變數在使用{ } 設定變數的生命週期,例如:
void fun1() {
int a;
{	int b;	}	//變數 b離開此大括號後系統及自動將其從stack釋放
}
           
  • 使用迴圈注意迴圈內的迴圈變數(i = 1 ; i < 10 ; i ++)的i,清楚迴圈內是否處理到相關變數而導緻當前的迴圈變數不同,例如:
for (i = 0 ; i < 10 ; i++){
...
if (i == 5)
	i = 9;	//在迴圈內另外操作迴圈變數,影響迴圈的運行次數,要注意流程!
...
}
           
  • 在函數內使用外部與函數內同名的全域變數,需在變數前加上

    ::

    即可使用,例如:
int var = 100;
fun(){
int var = 1000;
cout << var << endl;	//印出函數內的區域變數 var 1000
cout << ::var << endl;	//印出全域變數 var 100
}
           

指標的使用

  • 宣告指標時習慣初始化為

    null

    ,避免指向未知的記憶體空間而導緻不可預期錯誤
  • 宣告類別注意宣告成員變數的順序,會影響記憶體的對齊而影響類別大小(pacth對齊方式視編譯器設定)

    例如:假設編輯器為預設(可到專案屬性中> C/C++ > Code Genertaion > Struct Member Alignment 調整)

    註:類別的對齊大小(patch)一般情況下是視類別中最大的成員為基準,但若遇到系統多配置的空間,後面的元素可補上時,則會看是否對齊2 byte

class myClassA{
	// 宣告變數的記憶體位置對齊 (未設計過的變數宣告)
	int a;	//4 byte
	char b;	//1 byte(系統再補3 byte對齊)
	int c;	//4 byte
	char d;	//1 byte(系統再補3 byte對齊)
};

class myClassB{
	// 宣告變數的記憶體位置對齊 (設計過的變數宣告)
	int a;	//4 byte
	int b;	//4 byte
	char c;	//1 byte (系統再補 3 byte對齊)
	char d;	//1 byte (由於上述配有多餘空間,是以占用再上列所配置的空間,基於對齊方式是2的冪次方,上列c起碼會佔有2 byte,依順序來說是被安排在第11 byte的位置)
};

class myClassC{
	// 對齊標準PATCH 先以類別中最大的資料型態為標準,再以 2 的冪次方作為第二的對齊參考 (示範以此類別以最大資料型態為對齊標準)
	float c;	// 4 byte (系統自動補 4 byte)
	double a;	// 8 byte (類別中最大的類型,以此作為 PATCH 對齊標準)
	int b;		// 4 byte (系統自動補4 byte)
};

class myClassD{
	// 對齊標準PATCH 先以類別中最大的資料型態為標準,再以 2 的冪次方作為第二個的對齊參考 (示範2的冪次方為對齊方式)
	double a;	// 類別中最大資料型態 8 byte,作為對齊第一個標準
	char b;		// 1 byte (系統自動補 7 byte,加上以 2 的冪次方作為對齊的第二標準,是以至少占 2 byte,位在類別大小中的第 9、10 byte)
	int c;		// 4 byte (使用上列系統自動補的位置中,位在第 11~14 byte)
	short d;	// 2 byte (使用上列系統自動補的位置中,位在第 15、16 byte)
	char e;		// 1 byte (系統在自動補 7 byte)
};

int main(){
	myClassA A;	//A 記憶體大小為 16 byte
	myClassB B;	//B 記憶體大小為 12 byte
	myClassC C;	//C 記憶體大小為 24 byte
	myClassD D; //D 記憶體大小為 24 byte
}
           
  • 如果要將資料型態較大的變數存進STL容器中,建議使用

    指標

    的方式做存取,例如存入類別到容器中,若使用指標,該容器隻有所有指標的總和大小,若是放實體進去則是講整個類別的大小放進去,除了佔用大量記憶體,往後在做指標的運算也會佔用更多效能,例子如下:

    備註:容器的實際大小為 [容器大小] + [容器內的資料數量] * [容器內的資料大小]

class myClass{
int a,b,c,d;
};

int main(){
	myClass* myClassA = new myClass [5];	//動態宣告 5個 可存放myClass大小的記憶體空間,並由一個指標指向第一個物件
	
	vector<myClass>myClassVecA;
	for (int i = 0 ; i < 5 ; i++) {
		myClassVecA.push_back(myClassA [i]);
	}	//執行完迴圈後myClassVecB的實際大小 = 12byte(vector容器的大小) + 5(元素個數) * 16byte(類別大小) = 92 byte

	vector<myClass*>myClassVecB;
	for (int i = 0 ; i < 5 ; i++) {
		myClassVecB.push_back(&myClassA [i]);
	}	//執行完迴圈後myClassVecB的實際大小 = 12byte(vector容器的大小) + 5(元素個數) * 4byte(指標大小(依照系統而定)) = 32 byte
}
           

繼續閱讀