Delphi中那些容易混淆的基礎(@、^、Addr、Pointer,Move、CopyMemory,GetMem和FreeMem、GetMemory和FreeMemory、New和Dispose、StrAlloc和StrDispose、AllocMem)


@、^、Addr、Pointer

Delphi(Pascal)中有幾個特殊的符號,如@、^等,弄清楚這些符號的運行,首先要明白Delphi指針的一些基礎知識:指針,是一個無符號整數(unsigned int),它是一個以當前系統尋址范圍為取值范圍的整數。指針對應着一個數據在內存中的地址,得到了指針就可以自由地修改該數據。指針的指針就是用來存放指針所在的內存地址的。
明白了指針的基本含義,就容易理解它們之間 的區別了:

  • @XX:取變量、函數或過程XX的地址(獲取指針);
  • Addr(XX):和@的作用類似,唯一的不同在於如果編譯選項{$T-}沒有打開,@返回的是一個通用的指針,如果編譯選項打開了,@返回的是XX對應的指針,但Addr卻不受此編譯選項的約束。
  • ^:當它出現在類型定義的前面時如 ^typename 表示指向這種類型的指針; 當它出現在指針變量后邊時 如 point^ 返回指針指向的變量的值;
  • Pointer:無類型指針(對應PChar、PInteger等則為“有類型指針”)。

通過這段代碼則更容易區分它們:

復制代碼
var
X, Y: Integer;  // X and Y 整數類型
P: ^Integer;  // P 指向整數類型的指針
begin
X := 11;  // 給 X 賦值
P := @X;  // 把 x的地址賦給p
Y := P^;  // 取出p所指向的數值賦給y
end;
復制代碼

第二行定義了兩個變量X,Y。 第三行聲明了P是指向整數類型的指針;意味着P能夠指向X或者Y的地址。第五行賦給X值,第六行把X的地址賦給P。最后通過P指向的變量賦值給Y。此時,X和Y有相同的值。

Char、Byte

  • Char是一個字符,必須賦以字符如‘A’等;
  • Byte是無符號整數,數值范圍0~255。

雖然字符實質上也是整數,但與C不同,Delphi中將他們划為兩種不同的類型,各自遵從不同的運算。比如運算符+對於Byte是整數加法,對於Char則是字符連接成串。
他們可以相互轉化:Ord(‘A’)得到字符對應的整數,Chr(65)得到整數對應的字符。
同理,就很好區分array of Byte和array of Char了。

Move、CopyMemory

Move字面意思上是“移動”的意思,其實不然,在Delphi中Move更像是Copy:它可以復制一段內存片段到另外一段內存空間中。如下代碼:

復制代碼
var
  source, dest : string;
begin
  // Set up our starting string
  source := '123456789';
  dest   := '---------';
 
  // Copy a substring from source into the middle of dest
  Move(source[5], dest[3], 4);
 
  // Show the source and destination strings
  ShowMessage('Source = '+source);
  ShowMessage('Dest   = '+dest);
end;
//結果------------------
//Source = 123456789
//Dest   = --5678---
復制代碼
//而CopyMemory則可以在Delphi的源碼中看出端倪
procedure CopyMemory(Destination: Pointer; Source: Pointer; Length: DWORD);
begin
  Move(Source^, Destination^, Length);
end;

可以看出,CopyMemory其實也是調用了Move方法,但參數變了,CopyMemory參數是指針。當然,從源碼還可以發現MoveMemory和CopyMemory是一模一樣的功能。
CopyMemory一般的使用方法為:

復制代碼
var
  buf1,buf2: array[0..9] of AnsiChar;
begin
  buf1 := '0123456789';
  buf2 := 'abcdefghij';
  CopyMemory(@buf2[2], @buf1[4], 5);
  ShowMessage(buf1); {0123456789}
  ShowMessage(buf2); {ab45678hij}
end;
復制代碼

GetMem和FreeMem、GetMemory和FreeMemory、New和Dispose、StrAlloc和StrDispose、AllocMem

Delphi中的內存申請和釋放方法比較多,但有一點兒需要牢記的是上述方法建議配對使用。
GetMem和FreeMem與GetMemory和FreeMemory在Delphi的源碼中可以看到是被調用和調用的關系,FreeMemory會判斷指針是否為空:

復制代碼
function GetMemory(Size: Integer): Pointer; cdecl;
begin
  Result := MemoryManager.GetMem(Size);
end;
 
function FreeMemory(P: Pointer): Integer; cdecl;
begin
  if P = nil then
    Result := 0
  else
    Result := MemoryManager.FreeMem(P);
end;
復制代碼

因此,建議用GetMemory和FreeMemory代替GetMem和FreeMem。
New和Dispose是用來管理變體類型內存分配,如變體結構體:

TRecord = record
Text: string;
Value: Integer;
end;
PRecord = ^TRecord;

如果用GetMem和FreeMem、GetMemory和FreeMemory來釋放,會造成Text的內存沒有釋放,造成內存泄漏。如果用Dispose來釋放指針,要加上定義信息,否則造成內存泄漏,正確寫法Dispose(PRecord(Point))。另外需要注意的一點是:Delphi設計的Dispose釋放內存時,只是標記這部分內存可以再用來被New等函數分配,並不是把從系統申請到的內存歸還給操作系統,只在程序結束時,才全部釋放給操作系統,因此並不能在資源管理器中看到Dispose的“顯著效果”。
一般使用方法如下:

復制代碼
Type
 PMyRec = ^TMyRec;
 TMyRec = record
  FName: string;
  LName: string;
 end;
var
 MyRecPtr: PMyRec;
 TreeViewIndex: LongInt;
begin
 New(MyRecPtr);
 MyRecPtr^.FName := Edit1.Text;
 MyRecPtr^.LName := Edit2.Text;
 {其他處理}
 Dispose(MyRecPtr);
end;
復制代碼

StrAlloc和StrDispose這個函數也是一對,他們分配PChar加一個Cardinal長度,因此一定要用StrDispose釋放,否則容易造成4字節的內存泄漏。StrAlloc分配的指針可以使用StrBufSize來獲得大小。
AllocMem和GetMem的區別在於AllocMem在申請內存后會初始化這段內存(把內存全部初始化為#0),同樣和FreeMem配對使用。

https://www.cnblogs.com/chenmfly/p/4818347.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM