AnsiString VS Pchar

AnsiString?PChar?赋值?转换?

AnsiString,Delphi开发人员指南这么解释的,AnsiString就是指向堆中字符串结构的指针,显示了AnsiString的分配情况。
帮助上这么解释:长字符串保存有成员数量,PChar没有,长字符串(相对于ShortString类型来说,这里就是AnsiString)是Null结尾的,并且包含了引用记数,PChar就是一个简单的Null结尾的字符串。AnsiString之间的赋值是赋值数据,而PChar赋值是改变指针的指向。

同样存储LIKE,两种类型差异:

string简图:
--------------------------------------------------------
| 引用计数位 | 长度位 |  L | I | K | E | #0  |
--------------------------------------------------------

PChar简图:
-------------------------
|  L | I | K | E | #0  |
-------------------------

而string的指针通常指向数据位第一个,访问前面的引用计数和长度位的时候word ptr [eax-$04], dword ptr [eax-$08],这一类的代码。

它们之间的转换,Delphi开发人员指南里面是这么写的:不需要用StrPas和StrPCopy去来回转换string和PChar类型。我们在前面讲过,可以把AnsiString强制转化成PChar。在想把PChar转换成AnsiString时,可以直接赋值:StringVar := PCharVar;

StrPas的原型是什么?我悄悄地告诉你啊,不要告诉别人哦!:〉
function StrPas(const Str: PChar): string;
begin
  Result := Str;
end;
哈哈,搞笑吧。Borland弄了这么一个东西…… -_-bb
其实是为了兼容而设立的。

StrPCopy相对来讲就比较复杂了,是把string强制转化成PChar之后按照string的长度进行PChar之间复制。

function StrPCopy(Dest: PChar; const Source: string): PChar;
begin
  Result := StrLCopy(Dest, PChar(Source), Length(Source));
end;

而StrLCopy类似于C语言里面的strcpy这种用来复制内容的函数,所以尽量用StrLCopy来给PChar赋值,
因为会保护现场,用赋值号,就改变了指针的指向了。

有关Delphi开发人员指南里面的介绍,我写了这么一些代码来验证一下:
var
  P: PChar;
  S: string;
begin
  S := '1234567ABCDE';
  P := PChar(S); 
  S := P;
  ShowMessage(S);
  ShowMessage(P);
  ShowMessage(IntToStr(SizeOf(S)));
end;

当执行到S := '1234567ABCDE';时,汇编代码如下:

第一句:S := '1234567ABCDE';
0045B130 8D45FC           lea eax,[ebp-$04]
0045B133 BAB0B14500       mov edx,$0045b1b0
0045B140 E8E791FAFF       call @LStrLAsg

LStrLAsg就是字符串赋值地内建函数。正验证了帮助里面的那句string之间是数据Copy。

第二句:P := PChar(S);
0045B13D 8B45FC           mov eax,[ebp-$04]
0045B140 E8FF95FAFF       call @LStrToPChar
0045B145 8BD8             mov ebx,eax

进入这一句了,LStrToPChar是不是很容易理解呢,string转化为PChar。然后简单的赋地址,指针改变指向。

好了该由PChar转换到string了,这个是一个重点。

第三句:S := P;
0045B147 8D45FC           lea eax,[ebp-$04]
0045B14A 8BD3             mov edx,ebx
0045B14C E83393FAFF       call @LStrFromPChar

LStrFromPChar是由PChar转换string的一个内建函数。

进入LStrFromPChar的代码
@LStrFromPChar
00404484 31C9             xor ecx,ecx
00404486 85D2             test edx,edx
00404488 7421             jz +$21
0040448A 52               push edx
0040448B 3A0A             cmp cl,[edx]
0040448D 7417             jz +$17
0040448F 3A4A01           cmp cl,[edx+$01]
00404492 7411             jz +$11
00404494 3A4A02           cmp cl,[edx+$02]
00404497 740B             jz +$0b
00404499 3A4A03           cmp cl,[edx+$03]
0040449C 7405             jz +$05
0040449E 83C204           add edx,$04
004044A1 EBE8             jmp -$18     //循环得到字符串长度。
004044A3 42               inc edx
004044A4 42               inc edx
004044A5 42               inc edx
004044A6 89D1             mov ecx,edx
004044A8 5A               pop edx
004044A9 29D1             sub ecx,edx //到这里得到了PChar的长度。
004044AB E9D4FEFFFF       jmp @LStrFromPCharLen
004044B0 C3               ret

然后处理成string的长度的转化:

@LStrFromPCharLen
00404384 53               push ebx
00404385 56               push esi
00404386 57               push edi
00404387 89C3             mov ebx,eax
00404389 89D6             mov esi,edx
0040438B 89CF             mov edi,ecx
0040438D 89F8             mov eax,edi
0040438F E8C4FFFFFF       call @NewAnsiString

这里调用了NewAnsiString来生成一个新的标准字符串。
我们跳过来顺道看看string到底怎么生成的。string生成尽管是自动的,
其实和手工声称PChar差不多,不同的是比PChar多的长度位和引用计数的初始化。

@NewAnsiString
00404358 85C0             test eax,eax
0040435A 7E24             jle +$24
0040435C 50               push eax
0040435D 83C00A           add eax,$0a
00404360 83E0FE           and eax,-$02
00404363 50               push eax
00404364 E8B3E3FFFF       call @GetMem  //得到字符串长度后申请内存。
00404369 5A               pop edx
0040436A 66C74402FE0000   mov word ptr [edx+eax-$02],$0000
00404371 83C008           add eax,$08 
00404374 5A               pop edx
00404375 8950FC           mov [eax-$04],edx       //这里应该就长度位
00404378 C740F801000000   mov [eax-$08],$00000001 //这里是引用记数位
0040437F C3               ret
00404380 31C0             xor eax,eax
00404382 C3               ret
00404383 90               nop

继续LStrFromPCharLen下面的代码。
00404394 89F9             mov ecx,edi
00404396 89C7             mov edi,eax
00404398 85F6             test esi,esi
0040439A 7409             jz +$09
0040439C 89C2             mov edx,eax
0040439E 89F0             mov eax,esi
004043A0 E873E5FFFF       call Move      //然后把这个新的字符串的数据移到需要的字符串。
004043A5 89D8             mov eax,ebx
004043A7 E8E8FEFFFF       call @LStrClr  //清理NewAnsiString生成的临时字符串
004043AC 893B             mov [ebx],edi
004043AE 5F               pop edi
004043AF 5E               pop esi
004043B0 5B               pop ebx
004043B1 C3               ret

ShowMessage直接输入string就是直接调用。
ShowMessage(S);
0045B159 8B45FC           mov eax,[ebp-$04]
0045B15C E8638EFDFF       call ShowMessage

而输入PChar,嗬嗬,又调用了上面说到的LStrFromPChar然后才是调用ShowMessage,
其他的也是如此,如果输入参数没有转换,而是输入了PChar,那编译器会这么处理。
ShowMessage(P);
0045B161 8D45F8           lea eax,[ebp-$08]
0045B164 8BD3             mov edx,ebx
0045B166 E81993FAFF       call @LStrFromPChar
0045B16B 8B45F8           mov eax,[ebp-$08]
0045B16E E8518EFDFF       call ShowMessage

最后一句:ShowMessage(IntToStr(SizeOf(S)));
如果没有差错的话,就是4,一个指针的长度。

好了,一切都清晰了。
S := S;
使用了LStrLAsg内建函数来赋值。

P := P;
地址赋值。

P := PChar(S);
调用LStrToPChar转换为PChar之后地址赋值。

S := P;

这个最最麻烦,
调用LStrFromPChar来转化。
代码中首先取得PChar的长度,
然后调用LStrFromPCharLen用string的长度赋值,
代码中首先使用NewAnsiString在堆中生成一个临时字符串,
其次调用Move,Copy到目的字符串,
最后清理临时字符串。

PCharAnsiString的区别?AnsiString和windeString的区别?

06-23

这是D5中的一段。我不明白PChar和AnsiString的区别是什么。为什么要调用PChar(S)?还要用RealizeLenght()该回来?rnrn“注意在练习将一个字符串转换为PChar类型时要小心,因为字符串在超出其作用范围时有自动回收的功能,因此当进行P:=PChar(Str)的赋值时,P 的作用域(生存期)应当大于Str 的作用域。”rnrn这段话是什么意思?P的作用域如果不大于Str回怎么样?rnrnwindeString中的每个字符是2个字节?AnsiString每个字符是1个字节那AnsiString和windeString的为什么可以直接转换。转换后的字节数是怎么定?rnrn原文:rn正如前面所提到,AnsiString 字符串总是null 结束的。因此,它能跟以null 结尾的字符串兼容,这rn就使得调用Win32 API 函数或其他需要PChar 型字符串的函数变得容易了。只要把一个字符类型强制转rn换为PChar 类型(在2 . 8 节“强制类型转换和类型约定”中将介绍强制类型转换)。下面的代码演示了怎rn样调用Win32的GetWindowsDirectory( )函数,这个函数需要一个PChar类型的参数:rnvarrnS:String;rnbeginrnSetLength(S,256); //重要!首先给字符串分配空间rn/ /调用A P I 函数,S 现在包含目录字符串rnGetWindowsDirectory(PChar(S),256) ;rn如果使用了将A n s i S t r i n g 字符串强制转换为PChar类型的函数和过程,在使用结束后,要手工把它rn第2 章Object Pascal 语言2 7 下载rn的长度恢复为原来以n u l l 结束的长度。STRUTILS单元中的RealizeLenght( )函数可以实现这一点:rnprocedure RealizeLength(var S:string);rnb e g i nrnSetLength(S,StrLen(PChar(S)));rne n d ;rn调用ReallizeLength( ) :rnvarrnS:string ;rnbeginrnSetLength(S,256); //重要!首先给字符串分配空间rn/ /调用函数,S 现在包含目录字符串rnGetWindowDirectory(PChar(S),256) ;rnRealizeLength(S); //设置S 的长度为n u l l 结束的长度rne n d ;rn注意在练习将一个字符串转换为P C h a r 类型时要小心,因为字符串在超出其作用范围时有自动回收的功能,因此当进行P:=PChar(Str)的赋值时,P 的作用域(生存期)应当大于Str 的作用域。rn 论坛

AnsiStringPChar研究手记 (第一篇)

03-07

AnsiString和PChar研究手记rnrn前言:此文章来源于一次与aiirii的争吵,因为他不知道PChar的机理。而我嘲讽的语气最后造成了问题的不了了之。前几天DreamTheater大哥提到了有关这个方面的问题,我一向对大哥问题比较重视。经过研究,反思之后,其实当初我对这个了解仍旧是浅显的,其实当初给aiirii解释的也是浅显的,还是因为自己的浮躁。所以诞生了这篇文章,来解释AnsiString和PChar。rnrn约定:因为这篇文章就是讨论的AnsiString这种长字符串,而ShortString这种短字符串不在讨论范围之内。WideString是Unicode字符串,机理与AnsiString近似。所以,这里只对AnsiString做讨论。rnrn一:AnsiString与PChar的原理。rnrn首先来说说PChar,PChar的存在,是为了兼容 Win32 API 中所使用的C语言的字符串。C语言中声明一个字符串变量ch是这么声明的:rnChar *ch,rn当字符串变量建立之后字符串结尾会自动加上NULL字符。所以这种类型可以这么说:以NULL(#0)结尾的字符串指针。rn如果强加给PChar一个与C语言类似的Pascal的声明,那就是PChar: ^Char ,实际中这个声明是不会自动加上NULL结尾。所以Delphi把PChar独立成为一种类型。是的,PChar是一个指针,在使用指针变量之前,不可避免的要手动给变量分配内存,在使用完毕后释放这段内存。rnrn再来看看AnsiString,AnsiString是Delphi内建的字符串类型。本质是一个指向堆中字符串结构的指针。rnrnAnsiString有两部分组成,一部分是字符串头结构体,另一部分就是数据存储的部分。而在system.pas中这个头结构体定义如下:rntypern PStrRec = ^StrRec;rn StrRec = packed recordrn refCnt: Longint; //引用计数rn length: Longint; //字符串长度。rn end;rnrn而数据部分,类似一个存储字符的可增长数组,是个简单的顺序表,所以可以虚拟的下一个定义:rntypern TCharList = array[1..MaxListSize] of Char;rn AnsiString = ^TCharList;rnrnAnsiString的存储就是比PChar多了前面的这两个东西。引用计数和字符串长度。如果画一下简单的图例,保存LIKE这个字符串表示如下:rnrnPChar简图:rn------------------------rn| L | I | K | E | #0 |rn------------------------rnAnsiString简图:rn----------------------------------------------rn| 引用计数位 | 长度位 | L | I | K | E | #0 |rn----------------------------------------------rnrn引用计数的意义是:几个字符串可能指向同一个内存地址。所以这个地址就是保存这块内存有几个变量引用了它。rnDelphi内存管理使用Copy on write技术,只有在修改了其内容之后,才生成申请新的地址,生成新的串。rnrn 论坛

没有更多推荐了,返回首页