@、^、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