記憶體管理小結

2021-09-30 02:23:34 字數 3732 閱讀 8212

記憶體管理小結

版本:0.1

日期:2004-07-09

修改:這部分內存在程式執行的整個過程中是始終歸這個程式所有的,例如全域性資料、static變數等等。

定義的區域性變數使用的是棧記憶體,這部分記憶體歸作業系統管理。程式執行的時候動態分配,一旦這個變數超出作用域則被釋放。

以上兩中記憶體都是系統管理的,一般來說程式設計師甚至感覺不到他們的存在(如果不主動思考)。但是堆記憶體是由程式**來動態生成,同時又是由程式負責釋放的。所以這中型別的記憶體功能最為強大,使用最為靈活,也最容易出問題。這部分也是本文介紹的重點。

在c語言中,使用malloc()/free()函式來使用堆記憶體。在c++語言中提供了兩個操作符new/delete ,較之malloc/free,不但可以生成釋放記憶體空間,而且還可以執行構造和析構函式。

前面說過,malloc/free是兩個函式,因此是不受編譯器控制的。而new/delete是兩個可以受編譯器控制的操作符。malloc()函式執行的時候需要傳入乙個長度作為引數從而確定究竟需要分配多少記憶體,同時返回的指標也是沒有型別的,往往需要強制轉換為某種型別,例如:

char* p = (char*)malloc( sizeof(char) * 4);
在使用malloc分配記憶體的時候往往使用sizeof,這樣可以為特定型別分配剛好是這種型別整數倍的空間,當然也可以直接用乙個常量數字,但是這樣往往不方便使用。

記住每次使用完了malloc之後要用free()函式來釋放這個空間

free(p) //和上面的malloc成對使用
而new/delete,因為是操作符,所以編譯器知道應該分配多長的空間和應該返回什麼樣的指標。更重要的,執行new的時候,這種資料型別的建構函式也被執行,而delete這個物件的時候,他的析構函式被執行。因此可以用如下偽**來描述new和delete:

p = new type; 過程: if ( p = malloc(sizeof(type)) )  else  retur p; delete p; 過程: if ( p->~destructor())
這在物件導向開發的時候是非常方便的。

一般來說,用malloc()分配的空間用free()來釋放,用new分配的空間用delete來釋放。如果用free()來釋放new出的空間,則沒有執行析構函式,有可能會出問題。而如果用delete來釋放malloc()出的空間。也有可能會因為無法執行析構函式而出錯,例如:

cstring *pstr = (cstring*)malloc(sizeof(cstring)); delete pstr; //因為建構函式沒有執行,所以無法執行析構函式,這裡會導致程式崩潰
對於類似int、char等基本型別,雖然互換使用不會出錯,但是閱讀上也會感覺概念混亂。

記憶體洩漏屬於資源洩漏的一種,其他的還有控制代碼未釋放,連線未斷開等等。所謂洩漏就是這個資源已經不使用了,而作業系統確認為它是被使用的。常見的記憶體洩漏有如下幾種情況:

如果呼叫了malloc/new 而始終沒有呼叫free/delete,就導致了這塊記憶體始終被作業系統認為是使用的。這種情況是最基本的。

例子1使用new操作符可以一次分配多個物件的空間,釋放的時候必須都釋放。例如下面這段**中一次構造了5個char形物件(我們可以把char資料型別也看作是一種簡潔的物件),而用delete p 只能釋放乙個。必須使用delete來釋放。

char *p  = new char[5]; delete p; //錯誤 detete p; //正確
例子2

如果在乙個迴圈中分配,那麼必須同時在迴圈中釋放或者用迴圈來釋放,這也充分體現了new/delete必須「配對」使用的原則。例如下面的**,for()迴圈中分配的空間,一共分配了5次,必須對每次都釋放:

char * p = null; for ( i=0 ; i<5 ; i++ )  delete p; //錯誤
或者在迴圈中把這些指標存放到乙個陣列中最終集中釋放。

如果在執行分支外部分配的空間而在分支內部釋放,必須對於所有的分支都要執行釋放,下面的程式,函式執行失敗的時候釋放了記憶體,而執行成功的時候沒有釋放,屬於記憶體洩漏。

class aobj; aobj *pa = new aobj; if ( !pa->exce() )  //僅僅在分支內部釋放,而執行其他分支的時候會造成洩漏。 else
指未被釋放而且已經沒有指標指向這段空間的記憶體,是一種低階的錯誤。

char* getastring()
上面這種情況是非常恐怖的,因為申請的記憶體即使想釋放都不成了,已經沒有指向它的指標了。

有一些函式,在內部分配的空間用乙個指標返回,釋放的工作則留給了這個函式的呼叫者。例如:

createarect(cmyrect*& prect);
假設cmyrect是我們自己定義的乙個關於矩形的類,這個函式內部構造了乙個 cmyrect類的堆物件,這個物件由呼叫者使用並負責銷毀。

原則上來說,這種設計應該避免。如果這麼做了,應該有個與之對應的deletearect()之類的函式。但是因為arx特殊的資料庫機理,在arx開發中是一種比較常見的情況。這就要求我們必須高度注意乙個有新構造的函式的使用。

由於常量資料是存放在靜態棧記憶體中的,因此不需要程式專門釋放。上面講的,返回乙個字串,如果是常量型別,那麼是不需要釋放的。

在針對autocad 二次開發的object arx 開發中,物件的使用有著自己特定的一套機理,必須和c++物件的記憶體管理配合使用。並且務必做到概念清晰,才不至於出錯。

arx中的物件本質上也是乙個c++的類物件,但是arx自己有一套類庫,有自己的執行時庫。因此只有符合一定條件(從幾個特定的arx類派生)特定的類物件才是arx物件。

對於乙個.dwg檔案,我們稱之為乙個資料庫,這個資料庫中有多個arx物件,有一部分時必須存在的有些則是可選的。這些物件之間有特定的關係。

在arx中,加入資料庫是指把乙個物件按照arx特定關係和資料庫中原有的物件關聯起來。

加入資料庫的必須是乙個堆物件

就是說,對於這個物件的類,例如cmyrect,必須用如下的方法得到的物件才可以加入到資料庫。

cmyrect *prect = new cmyrect;
加入資料庫的物件不能直接釋放

按照c++的記憶體管理知識,對於這樣的指標必須有delete prect;這樣的語句。arx是c++的子集,當然也概莫能外。但是arx有自己的一套機制來處理這件事情,使得一旦這個指標所指的物件(一塊在堆上的記憶體)被加入資料庫中後,我們不能再使用delete來釋放這個物件,而要使用arx自己的關閉函式來釋放。

prect->close();
執行這個操作後,實際上堆記憶體的釋放工作是交給autocad來完成了。

這樣,對於arx開發,沒有加入資料庫中的物件使用c++的delete來釋放,加入資料庫中的物件使用close()來關閉。因此要求開發人員對於什麼時候應該delete,什麼時候應該close()時刻保持清醒。簡而言之,當且僅當加入資料庫的物件必須呼叫close()

arx庫函式返回指標的釋放

arx中有許多函式返回乙個指標,例如:

getdescription(char*& desc) const; //返回的desc需要釋放 getname(char*& name) const; //返回的name 需要釋放
result buffers指標要用特定的函式釋放

arx的result buffers是乙個特殊的鍊錶結構體,有許多函式,例如acutbuildlist()等,可以生成乙個此種型別的堆物件,都需要用如下方法釋放:

acutrelrb(result); //result是乙個 result buffer型別的指標

記憶體管理小結

棧區 可讀可寫 由編譯器自動分配釋放 存放函式的引數值,區域性變數 的值等。其 操作方式類似於資料結構中的棧 例 int main 堆區間 可讀可寫 在執行的時候呼叫程式 如c中的malloc或c 中的new 分配記憶體,可以在任何時候決定分配記憶體及分配的大小,使用者自己負責在何時釋放記憶體 如用...

ios 記憶體小結

這是 看 書籍 objective c 高階程式設計 ios與os x多執行緒和記憶體管理 一書的隨記。除錯 1.檢視 autoreleasepool 池裡面的物件 extern void objc autoreleasepoolprint objc autoreleasepoolprint 上面的...

記憶體對齊小結

原則1 結構體的資料成員,第乙個資料成員放到 0 的位置,以後每個資料成員的起始位置要從該成員大小或成員子成員大小 如陣列 的整數倍開始 原則2 結構體作為成員,如果乙個結構中有某些結構體成員,則結構體成員要從其內部最大的整數倍位址開始儲存 struct a中公有struct b,b中有char,i...