先回顾一下String类型的结构
var s: String; p1: PInteger; {与Free Pascal不同的是,Delphi中Integer与Longint同为32位带符号整数} p2: PSmallInt; {SmallInt则相当于Free Pascal里的Integer,表示16位带符号整数} begin s:= 'Hello World!'; p1:= PInteger(Cardinal(s)); dec(p1); write(p1^,' '); dec(p1); write(p1^,' '); p2:= PSmallInt(Cardinal(s)-8); dec(p2); {对有序类型的指针使用Inc(),Dec()时,地址的偏移量与数据类型的大小相对应} write(p2^,' '); dec(p2); {Integer类型的指针偏移4个字节,SmallInt类型的指针偏移2个字节} writeln(p2^,' '); end.
显示结果12 1 2 1200,分别表示字符串长度为12,引用数1,每个字符占2个字节,代码页1200。(注:UnicodeString默认使用UTF-16编码,而UTF-16 LE的代码页为1200)
在详细地观察引用数之前,先写一个显示引用数的过程:
procedure ShowRef(const s: String); begin if s = '' then writeln(0) else writeln(PInteger(Cardinal(s)-8)^); end;
事实上Delphi有一个现成的函数也可以得到引用数:
function StringRefCount(const S: String): Longint;
- String变量
String类型的变量在赋值的时候会把目标字符串的引用数加一,如果之前它指向了一个字符串的话则把它的引用数减一。当把字符串作为参数传递时,引用数加一。但如果参数有const,var等关键词修饰则引用数不变。
procedure Test1(s: String); begin ShowRef(s); end; procedure Test2(const s: String); begin ShowRef(s); end; begin s1:= 'Hello World!'; ShowRef(s1); {1} s2:= ''; ShowRef(s2); {0。空字符串变量指向的地址为0} s2:= s1; ShowRef(s1); {2。将s1赋给s2,引用数加一} Test1(s2); {3。把字符串作为参数传递时引用数加一} Test2(s2); {2。如果参数用const或者var修饰,则引用数不变} end.
不过如果这个变量是一个局部变量的话,情况就会不同。
procedure test3(s: String; n: Longint); begin ShowRef(s); if n > 0 then test3(s,n-1); end; procedure DoSth; var s,s1: String; begin s:= 'ABC'; test3(s,2); s1:= s; ShowRef(s); s:= s+'a'; test3(s,2); end; begin DoSth; end.
显示结果 -1 -1 -1 -1 2 3 4。对于局部变量,如果只是赋予它字面量的话,似乎它的引用数永远都是-1。一旦让它参与了一些运算,又会适用正常的规律。
- String常量
字符串常量的引用数永远为-1。当把一个字符串常量赋给变量时,程序会自动复制一份字符串给变量,并把引用数设为1。
const MyStr = 'ABC'; var s: String; begin s:= MyStr; ShowRef(s); //显示结果 1 ShowRef(MyStr); //显示结果 -1 end.