樊东东2004年9月10日
说明:本人不擅长文笔,只是自己的体会写下,有错误之处请指正,谢谢!
一、 String与ShortString的区别
ShortString是Delphi的传统的Pascal类型的字符串(区别与C类型的字符串),最多能存储255个字节。而String是Delphi中特有的字符串类型,并且自动管理内存分配和释放,可以说是一种智能的字符串,并且他能存储字符串的长度可以说是无限制的(内存的限制)。
二、 了解ShortString
学过Pascal的都知道,ShortString类型的字符串的第一个字节是字符串字符的个数,从第二个开始才是字符串的字符,并且ShortString是定长的,是256个字节,即sizeof(ShortString)是256,第一个字节是存储长度,所以只能存储255个字节。
三、 了解String
那String到底是什么样的结构呢?那sizeof(String)是多少呢?如果没有错的话,应该是4。也许大家会感到很惊讶,为什么始终为4呢?经过分析,原来String有两部分构成,第一部分即是4个字节长度的指针(就是sizeof(String)=4的那个),第二部分就是实际的字符串了,而第一部分的指针指向的就是第二部分的字符串。
举例:
var
String str
begin
str := ‘DELPHI’
end
取str的地址,即@str,我们会得到具体的地址$12F418(不同的环境可能不一样)。
然后我们查看地址为$12F418处的内存,我们就会发现’D’,’E’,’L’,’P’,’H’,’I’,’#0’字符。
这就是string的基本结构,可是string也并不是那么简单的,其实在在地址为$12F410开始就是string的第二部分,只是我们平时用不着关心而已,都是由编译器处理。从$12F410到$12F417的8个字节,其中前面四个字节记录字符串被引用的次数,如果是$FFFFFFFF则表示此字符串是常量。后面四个字节记录字符串的长度,不包括结束符#0。
四、 分析String
接下来我们用源码分析String。
1、 字符串的赋值
当我们给字符串赋值时,将会调用
procedure _LStrLAsg(var dest
const source)
asm
{ -> EAX pointer to dest }
{ EDX source }
TEST EDX,EDX
JE @@sourceDone { if source = nil then exit }
{ bump up the ref count of the source }
MOV ECX,[EDX-skew].StrRec.refCnt { 获取引用次数}
INC ECX
JLE @@sourceDone { literal assignment -> jump taken }{如果字符串是常量,则不进行引用计数}
LOCK INC [EDX-skew].StrRec.refCnt
@@sourceDone:
{ we need to release whatever the dest is pointing to }
XCHG EDX,[EAX] { fetch str }{给目的字符串赋值}
TEST EDX,EDX { if nil, nothing to do}{判断目的字符串是否首次赋值}
JE @@done { 如果是,则退出,如果不是,则根据目的字符串的原字符串的引用计数进行适当操作}
MOV ECX,[EDX-skew].StrRec.refCnt {fetch fCnt }
DEC ECX { if < 0: literal str}
JL @@done {如果目的字符串的原字符串是常量,则直接退出}
LOCK DEC [EDX-skew].StrRec.refCnt { threadsafe dec refCount}{目的字符串的原字符串不是常量, 目的字符串的原字符串引用计数减一}
JNE @@done
LEA EAX,[EDX-skew].StrRec.refCnt { if refCnt now zero, deallocate}
CALL _FreeMem{如果目的字符串的原字符串引用计数减一后等于零,则释放目的字符串的原字符串占用的内存}
@@done:
end
2、 取得字符串长度
function _LStrLen(const s: AnsiString): Longint
asm
{ -> EAX str }
TEST EAX,EAX
JE @@done {如果s是空字符串,则直接退出,返回值为0 }
MOV EAX,[EAX-skew].StrRec.length
{如果s的长度}
@@done:
end
3、 设置字符串长度
procedure _LStrSetLength{ var str: AnsiString
newLength: Integer}
asm
{ -> EAX Pointer to str }
{ EDX new length }
PUSH EBX
PUSH ESI
PUSH EDI
MOV EBX,EAX
MOV ESI,EDX
XOR EDI,EDI
TEST EDX,EDX
JLE @@setString
MOV EAX,[EBX]
TEST EAX,EAX
JE @@copyString
CMP [EAX-skew].StrRec.refCnt,1
JNE @@copyString
SUB EAX,rOff
ADD EDX,rOff+1
PUSH EAX
MOV EAX,ESP
CALL _ReallocMem
POP EAX
ADD EAX,rOff
MOV [EBX],EAX
MOV [EAX-skew].StrRec.length,ESI
MOV BYTE PTR [EAX+ESI],0
JMP @@exit
@@copyString:
MOV EAX,EDX
CALL _NewAnsiString
MOV EDI,EAX
MOV EAX,[EBX]
TEST EAX,EAX
JE @@setString
MOV EDX,EDI
MOV ECX,[EAX-skew].StrRec.length
CMP ECX,ESI
JL @@moveString
MOV ECX,ESI
@@moveString:
CALL Move
@@setString:
MOV EAX,EBX
CALL _LStrClr
MOV [EBX],EDI
@@exit:
POP EDI
POP ESI
POP EBX
end;