zoukankan      html  css  js  c++  java
  • Delphi

    来自:http://www.cnblogs.com/huangjacky/archive/2009/12/10/1620950.html

    ------------------------------------------------------------------------------------------

    技术交流,DH解说.

    以前写过一次,现在全部重写吧.比较基础了,高手莫笑.

    记得有次在盒子上面看到有个人出的面试题,第一题就是:
    AnsiString 和 WideString的区别.
    好这里先留给大家想想,我讲完了,大家就应该知道了.嘿嘿.

    首先分类:
    1 ShortString,可以容纳255个字符,主要为了老版本兼容
    2 AnsiString,可以容纳2的31次方个字符,D2009前默认的String类型
    3 UnicodeString,可以容纳2的30次方个字符,D2009及以后的默认String类型
    4 WideString,可以容纳2的30次方个字符,主要在COM中用的比较多.

    好一个一个来讲:
    ShortString
    我们看到上面说的它可以容纳255个字符,但是它所占的空间是字符长度加1,为什么?
    我们来看个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    Procedure TForm3.Btn1Click(Sender: TObject);
    Var
      S: String[15];
    Begin
      S:= 'HuangJackyAAAAA';
      ShowMessageFmt('%d', [Length(S)]);//15个字符
      ShowMessageFmt('%d', [SizeOf(S)]);//空间大小是16
    End;

    从上面的代码我们可以看出来,ShortString变量不是用的指针,而是直接就是内存块,不然SizeOf应该是4的.既然我们发现这个问题,我们肯定要去看下它的内存情况吧.
     image 
    我们可以发现它第一个字节用来存放的字符串的长度,所以它只能容纳255个字符,$FF,是吧.
    那么把上面的代码改一下:

    1
    2
    3
    4
    5
    6
    7
    8
    Procedure TForm3.Btn1Click(Sender: TObject);
    Var
      S: String[15];
    Begin
      S:= 'HuangJackyAAAAA';
      ShowMessageFmt('%d', [ord(S[0])]);//同样是15,哈哈.
      ShowMessageFmt('%d', [SizeOf(S)]);
    End;

    好的,这样我们已经掌握了第一种字符串了.

    AnsiString
    用同样的代码测试:

    1
    2
    3
    4
    5
    6
    7
    8
    Procedure TForm3.Btn1Click(Sender: TObject);
    Var
      S: AnsiString;
    Begin
      S:= 'HuangJackyAAAAA';
      ShowMessageFmt('%d', [Length(S)]);//15
      ShowMessageFmt('%d', [SizeOf(S)]);//4
    End;

    说明变量S只是一个指针.
    image 
    好去看看这个地址:
     image 
    是吧,那么存放实际字符串这块内存是怎么样组织的呢?

    偏移 -12 -10 -8 -4 0-长度-1 最后一位
    内容 字符页码 每个字符大小 引用次数 字符串长度 实际内容 0

    名词解释下:页码是什么?编码,UTF-8或者GBK这些.
    我们知道字符串其实是一个对象,但是它的释放却不需要我们操心,那就是因为当引用次数为0的时候,编译器会自动释放.
    我们上面说了AnsiString能容纳2的31次方个字符,是的,因为它的长度占了4个字节.
    好我们写代码来测试一下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Procedure TForm3.Btn1Click(Sender: TObject);
    Var
      S: AnsiString;
      I:Integer;
    Begin
      S:= 'HuangJackyAAAAA';
      I:=Integer(S);
      ShowMessage(IntToHex(PWord(I-12)^,4));//$03A8
      ShowMessage(IntToHex(PWord(I-10)^,4));//$0001,AnsiString中一个元素就一个字节
      ShowMessage(IntToHex(PCardinal(I-8)^,8));//$FFFFFFFF
      ShowMessage(IntToHex(PCardinal(I-4)^,8));//$0000000F
      ShowMessageFmt('%d', [Length(S)]);//15
      ShowMessageFmt('%d', [SizeOf(S)]);//4
    End;

    来贴图一张:
    image 
    和我们上面说的一样吧.
    我们再去看下页码对应的是什么编码.$03A8就是936,查MSDN  936 - gb2312.哈哈.
    UnicodeString的内存分布也是一样的.所以我感觉-12和-10加入主要为了UnicodeString服务的.因为Unicode里面的编码就很多了,每个元素的大小也根据编码不同有所不同的.
    进入WideString之前,我们看看Delphi里面Length函数是怎么实现的.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    Unit3.pas.40: I:=Length(S);
    004B33CD 8B45FC           mov eax,[ebp-$04]
    004B33D0 85C0             test eax,eax
    004B33D2 7418             jz $004b33ec
    004B33D4 8BD0             mov edx,eax
    004B33D6 83EA0A           sub edx,$0a
    004B33D9 66833A01         cmp word ptr [edx],$01
    004B33DD 740D             jz $004b33ec
    004B33DF 8D45FC           lea eax,[ebp-$04]
    004B33E2 33C9             xor ecx,ecx
    004B33E4 8B55FC           mov edx,[ebp-$04]
    004B33E7 E8782EF5FF       call @InternalLStrFromUStr//因为我们不是用的Unicode所以这里会被跳过
    004B33EC 85C0             test eax,eax
    004B33EE 7405             jz $004b33f5
    004B33F0 83E804           sub eax,$04 //对就是这样,偏移-4
    004B33F3 8B00             mov eax,[eax] //取得它的值.是吧
    004B33F5 8BD8             mov ebx,eax

    反编译代码比Delphi7多太多了...
    刚才在翻VCL代码的时候发现这样一个结构体:就是我们刚才说的

    1
    2
    3
    4
    5
    6
    StrRec = packed record
      codePage: Word;
      elemSize: Word;
      refCnt: Longint;
      length: Longint;
    end;
    1
      

    WideString
    从名字看,我们就知道了它一个字符肯定占2个字节了.哈哈,看例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Procedure TForm3.Btn1Click(Sender: TObject);
    Var
      S: WideString;
    Begin
      S:= 'HuangJackyAAAAA';
      ShowMessageFmt('%d', [Length(S)]);//15
      ShowMessageFmt('%d', [SizeOf(S[1])]);//2
      ShowMessageFmt('%d', [SizeOf(S)]);//4
    End;

    是吧,每个字符是2个字节,变量还是存放的指针.像AnsiString那样看看它实际数据在内存中的组织:

    偏移 -4 0~长度-1 最后一个
    内容 字符串长度 实际字符串内容 $00 $00

    它同样是用4个字节来存储长度,但是它每个元素的大小是2个字节,所以它最多只能存储2的30次方个字符.

    是不是想跑去看它的内存呢?
    image 
    长度是$1E??哈哈,长度要除2塞,因为这个长度是这个内存块的长度.

    最后再多说一个:
    PAnsiChar,PWideChar
    这个就是C++里面的Char*,也就是末尾是0的那种字符,这个就比较单纯了,没有什么用专门的字节来存储长度,存储引用次数.所以很多地方我们能用PChar就用PChar,因为String类型的确很耗资源.
    C++的人笑了,不要笑,CString一样有这个问题.
    看个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    Procedure TForm3.Btn1Click(Sender: TObject);
    Var
      S: PAnsiChar;
    Begin
      S:= 'HuangJackyAAAAA';
      ShowMessageFmt('%d', [StrLen(S)]);//15
      ShowMessageFmt('%d', [SizeOf(S^)]);1
    End;

    看下内存,证明我没有忽悠人.
    image 
    前面和它不沾边了吧,尾巴是0结尾了吧.

    开篇时候提的问题,大家也知道区别了吧.平时不要看字符串简单,其实我们了解得还不够.

    从上面看出来,字符串使用的使用第一个元素下标是从1开始的不是0.其他使用的注意事项打算写在下一篇文章里面.

    我是DH,貌似该吃午饭了.

  • 相关阅读:
    设置win 10 软件开机启动
    win10 无法使用内置管理员账户打开应用
    设置 npm 源为淘宝镜像
    git sh.exe 乱码
    mybatis 获取新增数据的主键
    框架 get 请求乱码
    js在浏览器下的区别小结(部分)
    js调试的一点小知识
    git常用命令
    css水平居中(一)
  • 原文地址:https://www.cnblogs.com/del88/p/5411890.html
Copyright © 2011-2022 走看看