標籤:sem 運行 常量 記憶體越界 highlight 等於 toc str base
一.記憶體的常見分配方式
1. 從靜態區分配,一般是全域變數和static類型變數
2.從棧區分配記憶體,一般是局部的變數,會隨著所在函數的結束而自動釋放
3.從堆中分配,一般是使用手動分配,使用malloc()函數和new來申請任意大小空間,不過要手動釋放空間,相應的使用free()函數和delete釋放,
如果不釋放該空間,而且指向該空間的指標指向了別的空間.則該空間就無法釋放,造成記憶體泄露,造成了記憶體浪費
二.記憶體的使用規則
1.在使用malloc()或new申請空間時,要檢查有沒有分配空間成功,判斷方法是判斷指標是否為NULL,如申請一塊很大的記憶體而沒有這麼大的記憶體則分配記憶體會失敗
2.申請成功後最好是將該記憶體清空,使用memset()後ZeroMemory()清空,不然存在垃圾而造成有時候輸出很大亂碼
3.不要忘記為數組和動態記憶體賦初值。防止將未被初始化的記憶體作為右值使用。(這句話不太理解)
4.要防止數組或指標記憶體越界,
5.申請記憶體成功後,使用結束後要釋放,系統不會自動釋放手動分配的記憶體
6.記憶體釋放後,指標還是指向那塊地址,不過這指標已經是"野指標"了,所以釋放記憶體後指標要指向NULL,不然很危險,容易出錯,if()對野指標的判斷不起作用
三.指標和數組
1. 數組裡的資料可以單個修改,但指標的不行,如我的例子,char str[] = "hello",數組的大小有6個字元(注意\0),可以通過str[0] = ‘X‘修改了的個字元,而指標
char *p = "Word",p是指向了一串常量的字串,常量字串是不可修改的,如 p[0] = ‘X‘,編譯器編譯時間不會儲存,但執行時會出錯
2.內容的複製與比較
內容的複製要使用strcpy()函數,不要使用賦值符"=",內容的比較也是不要使用比較符號"<,>,==",使用strcmp()函數
[cpp] view plain copy
- // 數組…
-
- char a[] = "hello";
-
- char b[10];
-
- strcpy(b, a); // 不能用 b = a;
-
- if(strcmp(b, a) == 0) // 不能用 if (b == a)
[cpp] view plain copy
- // 指標…
-
- int len = strlen(a);
-
- char *p = (char *)malloc(sizeof(char)*(len+1));
-
- strcpy(p,a); // 不要用 p = a;
-
- if(strcmp(p, a) == 0) // 不要用 if (p == a)
3,計算空間的大小
對數組的計算是使用sizeof()函數,該函數會按照記憶體對齊的方式4的倍數計算,而指標的空間大小沒法計算,只能記住在申請空間時的空間大小
注意當數組作為函數的參數進行傳遞時,該數組自動退化為同類型的指標,不論數組a的容量是多少,sizeof(a)始終等於sizeof(char *)
[cpp] view plain copy
- void Func(char a[100])
-
- {
-
- cout<< sizeof(a) << endl; // 4位元組而不是100位元組
-
- }
四.指標的記憶體的傳遞
如果函數的參數是指標,則不要使用該參數來申請記憶體空間,這樣沒有實際的用處,而且這樣當函數結束時還得不到釋放記憶體而造成記憶體泄露
這個問題可以使用"指標的指標"的方法可以解決,不然使用返回指標地址的辦法,先看一下使用 "指標的指標"方法,
還可以考慮一下引用
使用返回記憶體位址的方法
使用返回的方式傳遞記憶體位址容易出錯的地方在於放回"棧記憶體"的指標,當GetMemory()函數結束時棧記憶體也被釋放,
像這個代碼
[cpp] view plain copy
- char *GetString2(void)
-
- {
-
- char *p = "hello world";
-
- return p;
-
- }
-
- void Test5(void)
-
- {
-
- char *str = NULL;
-
- str = GetString2();
-
- cout<< str << endl;
-
- }
-
函數Test5運行雖然不會出錯,但是函數GetString2的設計概念卻是錯誤的。因為GetString2內的“hello world”是常量字串,位於靜態儲存區,
它在程式生命期內恒定不變。無論什麼時候調用GetString2,它返回的始終是同一個“唯讀”的記憶體塊。
五.動態記憶體釋放問題與野指標
1. 當我們使用free()和delete釋放一塊記憶體時,指標還是指向原來的地址,不過這時候的指標時野指標,
可以驗證一下.這圖是我調試到if()語句時的情況,p還沒有指向NULL,只是釋放了p指向的空間了
執行的結果可以看看...
所以有這樣的一些特徵:
1.指標銷毀了,並不表示所指的空間也得到了釋放 :記憶體泄露
2.記憶體被釋放了,並不表示指標也被銷毀了或指向NULL :野指標
六.malloc()/free()與new/delete的區別(摘抄原文)
malloc與free是C++/C語言的標準庫函數,new/delete是C++的運算子。它們都可用於申請動態記憶體和釋放記憶體。對於非內部資料類型的對象而言,
光用maloc/free無法滿足動態對象的要求。對象在建立的同時要自動執行建構函式,對象在消亡之前要自動執行解構函式。由於malloc/free是庫函
數而不是運算子,不在編譯器控制許可權之內,不能夠把執行建構函式和解構函式的任務強加於malloc/free。
因此C++語言需要一個能完成動態記憶體分配和初始化工作的運算子new,以及一個能完成清理與釋放記憶體工作的運算子delete。注意new/delete不是庫函數。
我們先看一看malloc/free和new/delete如何?對象的動態記憶體管理,看代碼
[cpp] view plain copy
- class Obj
-
- {
-
- public :
-
- Obj(void){ cout << “Initialization” << endl; }
-
- ~Obj(void){ cout << “Destroy” << endl; }
-
- void Initialize(void){ cout << “Initialization” << endl; }
-
- void Destroy(void){ cout << “Destroy” << endl; }
-
- };
-
- void UseMallocFree(void)
-
- {
-
- Obj *a = (obj *)malloc(sizeof(obj)); // 申請動態記憶體
-
- a->Initialize(); // 初始化
-
- //…
-
- a->Destroy(); // 清除工作
-
- free(a); // 釋放記憶體
-
- }
-
- void UseNewDelete(void)
-
- {
-
- Obj *a = new Obj; // 申請動態記憶體並且初始化
-
- //…
-
- delete a; // 清除並且釋放記憶體
-
- }
-
類Obj的函數Initialize類比了建構函式的功能,函數Destroy類比了解構函式的功能。函數UseMallocFree中,由於malloc/free不能執行建構函式與解構函式,必須調用成員函數Initialize和Destroy來完成初始化與清除工作。函數UseNewDelete則簡單得多。
所以我們不要企圖用malloc/free來完成動態對象的記憶體管理,應該用new/delete。由於內部資料類型的“對象”沒有構造與析構的過程,對它們而言malloc/free和new/delete是等價的。
既然new/delete的功能完全覆蓋了malloc/free,為什麼C++不把malloc/free淘汰出局呢?這是因為C++程式經常要調用C函數,而C程式只能用malloc/free管理動態記憶體。
如果用free釋放“new建立的動態對象”,那麼該對象因無法執行解構函式而可能導致程式出錯。如果用delete釋放“malloc申請的動態記憶體”,理論上講程式不會出錯,但是該程式的可讀性很差。所以new/delete必須配對使用,malloc/free也一樣。
七.如何處理記憶體耗盡
1.判斷指標是否為NULL,如果是則馬上用return語句終止本函數
2.判斷指標是否為NULL,如果是則馬上用exit(1)終止整個程式的運行
3.為new和malloc設定異常處理函數。例如Visual C++可以用_set_new_hander函數為new設定使用者自己定義的異常處理函數,
也可以讓malloc享用與new相同的異常處理函數
malloc()/free()和new/delete的使用要點網上有更詳細的說明
c++動態記憶體管理