Delphi中的字符串[二]——引用计数

先回顾一下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.

 

上一篇:delphi 中locate函数的使用方法


下一篇:Delphi-TScreen表示应用程序运行时屏幕的状态