天天看點

變量的記憶體配置設定和釋放

從作用範圍的角度,變量可以分為兩大類:全局(Global)變量和局部(Local)變量。

函數或者過程内部定義的變量為局部變量;其他的變量被聲明在interface和implementation部分,稱作全局變量,可以在整個單元中引用。對于在類中聲明的變量,如果我将類比作單元,那麼類中的變量可以比作單元中的全局變量;類的方法中聲明的變量可以比作函數和過程中的局部變量。以下所講的記憶體配置設定形式對于類中的變量也是适用的。

變量的記憶體配置設定形式有兩種:自動和人工。所謂自動配置設定,是一個變量被聲明後即被配置設定記憶體;而人工配置設定是指變量被聲明後必須用代碼顯式地配置設定記憶體。

一般地,無論是全局變量還是局部變量,如果它是非指針類型的,則聲明後被自動配置設定記憶體。如果是全局變量,還會被初始化為0:數值類型的為0,布爾類型的為False,字元的為”,等等。如果是局部變量,則不會被初始化,是以,它的值是不确定的(取決于别的程式對這塊記憶體作過的操作)。對于非Variant和File類型的全局變量,還可以在聲明時指定初始值(如:var I: Integer = 7;),但是對任何的局部變量都不可以這麼做。

如果變量是指針類型的,則不會被自動配置設定記憶體。如果它是全局的,則其初始值是nil,表示還沒有指向;如果是局部的,盡管沒有被配置設定記憶體,但是會随機地指向一個位址,是以值不是nil。

為了驗證上述内容,我需要舉幾個例子。

例1  驗證全局變量的記憶體配置設定形式:

var

  Global_Int: Integer;   {聲明非指針類型的全局變量Global_Int}

  Global_P: PChar;       {聲明指針類型的全局變量Global_P}

procedure TForm1.Button1Click(Sender: TObject);

begin

  if @Global_Int <> nil then    

{用@Global_Int取得Global_Int的位址指針,然後和nil比較。此條件為True}

  begin

    ShowMessage('Global_Int已被配置設定記憶體');

    ShowMessage(IntToStr(Global_Int));       {顯示初始值0}

  end;

  if Global_P = nil then                     {此條件也為True}

  begin

    ShowMessage('Global_P還沒被配置設定記憶體');

    ShowMessage(Global_P);      

{期望顯示Global_P指向位址處儲存的字元串,但因為指向不存在,是以傳回空字元串''}

  end;

end;

例2  驗證局部變量的記憶體配置設定形式:

procedure TForm1.Button1Click(Sender: TObject);

var

  Local_Int, Local_Int2: Integer;

  OldAddr, NewAddr: Integer;

  Local_P: PChar;

begin

  OldAddr := Integer(@Local_Int);    {取得Local_Int聲明後的位址}

  Local_Int := 7;                    {給Local_Int指派}

  NewAddr := Integer(@Local_Int);    {取得Local_Int被指派後的位址}

  if OldAddr = NewAddr then          {這個條件為True}

    ShowMessage  ('位址值沒有變化,是以聲明Local_Int時就配置設定記憶體');

  ShowMessage(IntToStr(Local_Int2));{非指針局部變量的初始值不是0}

  if Local_P <> nil then             {這個條件也為True}

    ShowMessage(Local_P);            {顯示的不是空字元串而很可能是亂碼}

end;

綜上所述,無論是全局變量還是局部變量,非指針類型的變量是被自動配置設定記憶體的,這個工作由編譯器編譯時完成,是以這種配置設定方式也被稱作靜态配置設定。靜态配置設定時,全局變量的記憶體配置設定在全局變量區,局部變量配置設定在應用程式棧(Stack)。它們的記憶體釋放工作也被自動管理,不需要程式員幹預。

注意:應用程式可用的記憶體區分為三類:全局變量區(專門用來存放全局變量)、棧(Stack)和堆(Heap)。應用程式開始運作時,所有全局變量的記憶體被配置設定到全局變量區,應用程式結束時被釋放;被配置設定在棧上的變量記憶體可被棧管理器自動釋放;堆上的變量記憶體必須人工釋放。

一般而言,對于指針類變量,則需要程式員使用一些代碼來完成記憶體配置設定,通常,這樣的配置設定方式也被稱作動态配置設定。但是也有一些指針類型的變量是被動态配置設定記憶體的,它們是:

長字元串(AnsiString/String)、寬字元串(WideString)、動态數組(dynamic arrays)和接口(interface)。這些類型的變量也是被自動釋放記憶體的。動态數組和接口也可以人工釋放記憶體,方法是指派nil。

完成具體配置設定的方法主要有下列一些:

(1)指派。其原理是将變量指向一塊已經存在的記憶體。這種方法适用于所有的指針類型。比如:

var

  P1,P2: PChar;

begin

  P1 := 'lxpbuaa';   {P1已經擁有一塊記憶體}

  P2 := P1;          {将P2指向P1的記憶體,這樣就間接完成了P2的記憶體配置設定}

end;

這種方法的本質是多個指針共享一塊已有的記憶體,是以,通過操作任何一個指針都可以達到記憶體釋放的目的。如果該塊記憶體是被自動管理的,那麼就不需要人工釋放。

(2)對于類,則調用構造函數。比如:

var

  Obj: TObject;

begin

  Obj := TObject.Create; {調用構造函數建立對象,變量Obj指向該對象}

  Obj.Free;              {釋放記憶體,Free内部調用析構函數Destroy;也可以使用

                                    FreeAndNil(Obj);}

end;

對象變量所指向的記憶體是必須人工釋放的,因為該塊記憶體被配置設定在堆(Heap)而不是棧(Stack)上。釋放對象記憶體時,應該調用析構函數(通常調用析構函數的包裝方法Free或者全局過程FreeAndNil即可)。

(3)配置設定指定大小的記憶體塊。主要用于建立緩沖區,一些函數和過程通過緩沖區傳回一些執行結果。比如檔案讀寫、流讀寫以及大量的API函數。我們看一個API函數使用緩沖區的例子,該函數可以取得計算機名字:

var

  P: PChar;

  Size: Cardinal;

begin

  Size := MAX_COMPUTERNAME_LENGTH + 1;

  GetMem(P, Size);          {配置設定Size個位元組的記憶體塊(即緩沖區),并讓P指向它}

  GetComputerName(P, Size);  {API函數GetComputerName将取得的計算機名放在P

                                        中}

  ShowMessage(P);

  FreeMem(P);                {釋放緩沖區占用記憶體}

end;

在上例中,我使用了GetMem過程來建立記憶體塊,并用FreeMem來釋放它。總結一下,動态配置設定記憶體的函數和過程有以下一些,它們都是在堆中配置設定記憶體,是以必須釋放:

(1)GetMem:

procedure GetMem(var P: Pointer; Size: Integer);

配置設定大小為Size位元組的記憶體塊,并讓P指向它。

(2)AllocMem:

function AllocMem(Size: Cardinal): Pointer;

配置設定大小為Size位元組的記憶體塊并初始化為零,并傳回位址指針。

如果希望在中途改變先前用GetMem或者AllocMem配置設定的記憶體大小,可以使用ReallocMem:

procedure ReallocMem(var P: Pointer; Size: Integer);

使用GetMem和AllocMem配置設定的記憶體都應該用FreeMem釋放:

procedure FreeMem(var P: Pointer);

(3)New:

procedure New(var P: Pointer);

用New配置設定的記憶體塊大小由參數P的類型确定,是以,不要使用它給無類型指針(即Pointer類型)變量配置設定記憶體。釋放該記憶體塊時使用Dispose:

procedure Dispose(var P: Pointer);

小結

本小節詳細讨論了變量記憶體配置設定的兩種形式。重點是:

(1)全局變量和局部變量的記憶體配置設定異同。

(2)變量記憶體配置設定和釋放什麼時候是自動/人工的。

(3)如何人工配置設定和釋放變量記憶體。  

繼續閱讀