//String 的指針地址及實際的內存地址
一個字符串(AnsiString 或 String, 譬如是 "Form1" )在內存中是這樣儲存的:
var
str: string;
pstr: PString;
pc: PChar;
begin
{在沒有給 str 賦值以前, 既然聲明了, 就有了指針地址(@str):}
ShowMessage(IntToStr(Integer(@str))); {1244652; 這是在棧中的 str 的指針地址}
{但現在還沒有分配真正儲存字符串內存}
ShowMessage(IntToStr(Integer(str))); {0; 0 就是 null}
str := 'Delphi';
{一旦賦值後...}
ShowMessage(IntToStr(Integer(@str))); {1244652; 這是在棧中的 str 的指針地址}
ShowMessage(IntToStr(Integer(str))); {4580800; 這是在堆中的 str 的實際地址}
{通過指針地址獲取字符串, 其中的 pstr 是前面定義的字符串指針}
pstr := @str;
ShowMessage(pstr^); {Delphi}
{通過實際地址獲取字符串, 其中的 pc 是前面定義的字符指針}
pc := PChar(Integer(str));
ShowMessage(pc); {Delphi}
end;
黃色區域是真正存字符串的位置, 前面說的字符串所在的內存地址, 就是本例中的 "F" 所在的位置;
藍色的四個字節儲存一個 Integer 值, 表示字符串的長度;
最後紅色的一個字節儲存一個空字符(#0), 表示字符串的結束, 同時也是為了和 Windows 的 null 結束的字符串兼容;
綠色的四個字節也是一個 Integer 值, 表示該字符串被引用的次數(也就是有幾個字符串的指針指向它).
還是看例子吧:var
當某段字符串內存的引用計數為 0 時, Delphi 就會自動釋放它; 這也是字符串不需要手動釋放的原因.
str,s1,s2: string;
pint: PInteger;
begin
str := Self.Text; {把窗體標題給它吧; 現在 str 指向了窗體標題所在的內存位置}
s1 := str; {給 s1 賦值}
s2 := str; {給 s2 賦值; 現在窗體標題已經有了 str、s1、s2 三個引用}
{str、s1、s2 的指針肯定不一樣; 但現在指向內存的同一個位置, 測試:}
ShowMessage(IntToStr(Integer(str))); {15190384}
ShowMessage(IntToStr(Integer(s1))); {15190384}
ShowMessage(IntToStr(Integer(s2))); {15190384}
{向左偏移 4 個字節就是字符串長度的位置, 讀出它來(肯定是5):}
pint := PInteger(Integer(str) - 4);
ShowMessage(IntToStr(pint^)); {5}
{向左偏移 8 個字節就是字符串的引用計數, 讀出它來(肯定是3):}
pint := PInteger(Integer(str) - 8);
ShowMessage(IntToStr(pint^)); {3}
end;
我在測試時發現: 所有常量和非全局的變量的引用計數一直是 "-1".