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

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

技术交流,DH解说.

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

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

首先分类:
ShortString,可以容纳255个字符,主要为了老版本兼容
AnsiString,可以容纳2的31次方个字符,D2009前默认的String类型
UnicodeString,可以容纳2的30次方个字符,D2009及以后的默认String类型
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的.既然我们发现这个问题,我们肯定要去看下它的内存情况吧.
  
我们可以发现它第一个字节用来存放的字符串的长度,所以它只能容纳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只是一个指针.
 
好去看看这个地址:
  
是吧,那么存放实际字符串这块内存是怎么样组织的呢?

偏移 -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;

来贴图一张:
 
和我们上面说的一样吧.
我们再去看下页码对应的是什么编码.$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次方个字符.

是不是想跑去看它的内存呢?
 
长度是$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;

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

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

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

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

Delphi - 字符串 详解的更多相关文章

  1. Delphi指针详解

    Delphi指针详解2007-12-04 06:08:57|  分类: DLL学习 阅读91 评论0   字号:大中小 订阅 大家都认为,C语言之所以强大,以及其自由性,很大部分体现在其灵活的指针运用 ...

  2. Delphi 关键字详解

    Delphi 关键字详解[整理于 "橙子" 的帖子] absolute //它使得你能够创建一个新变量, 并且该变量的起始地址与另一个变量相同. var   Str: ];   S ...

  3. SQL Server日期时间格式转换字符串详解

    本文我们主要介绍了SQL Server日期时间格式转换字符串的相关知识,并给出了大量实例对其各个参数进行对比说明,希望能够对您有所帮助. 在SQL Server数据库中,SQL Server日期时间格 ...

  4. Swift_字符串详解(String)

    Swift_字符串详解(String) 类型别名 //类型别名 fileprivate func testTypeAliases() { let index = String.Index.self p ...

  5. MS SQL Server 数据库连接字符串详解

    MS SQL Server 数据库连接字符串详解 原地址:http://blog.csdn.net/jhhja/article/details/6096565 问题 : 超时时间已到.在从池中获取连接 ...

  6. Python变量和字符串详解

    Python变量和字符串详解 几个月前,我开始学习个人形象管理,从发型.妆容.服饰到仪表仪态,都开始做全新改造,在塑造个人风格时,最基础的是先了解自己属于哪种风格,然后找到参考对象去模仿,可以是自己欣 ...

  7. C语言中字符串详解

    C语言中字符串详解 字符串时是C语言中非常重要的部分,我们从字符串的性质和字符串的创建.程序中字符串的输入输出和字符串的操作来对字符串进行详细的解析. 什么是字符串? C语言本身没有内置的字符串类型, ...

  8. Delphi多线程详解

    (整理自网络) Delphi多线程处理 1-1多线程的基本概念 WIN 98/NT/2000/XP 是个多任务操作系统,也就是:一个进程可以划分为多个线程,每个线程轮流占用CPU 运行时间和资源,或者 ...

  9. String 字符串详解 / 常用API

    String 详解 / 常用API 简介 String 是不可改变的字符串序列.String 为字符串常量 StringBuilder 与StringBuffer 均为可改变的字符串序列.为字符串变量 ...

随机推荐

  1. elasticsearch 6.2.3安装ik分词

    下载 zip文件 上传到服务器 https://github.com/medcl/elasticsearch-analysis-ik/releases unzip elasticsearch-anal ...

  2. javascript获取和判断浏览器窗口、屏幕、网页的高度、宽度等

    主要介绍了javascript获取和判断浏览器窗口.屏幕.网页的高度.宽度等 scrollHeight: 获取对象的滚动高度.scrollLeft:设置或获取位于对象左边界和窗口中目前可见内容的最左端 ...

  3. 【python】Python中给List添加元素的4种方法分享

    List 是 Python 中常用的数据类型,它一个有序集合,即其中的元素始终保持着初始时的定义的顺序(除非你对它们进行排序或其他修改操作). 在Python中,向List添加元素,方法有如下4种方法 ...

  4. 【bzoj2957】楼房重建 分块+二分查找

    题目描述 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多少栋房子.为了简化问题,我们考虑这些事件发生在一个二 ...

  5. 不使用库函数、自己编写的(strlen、strcpy、strcmp、strcat、memcmp、memcpy、memmove)

    不使用库函数.自己编写的(strlen.strcpy.strcmp.strcat.memcmp.memcpy.memmove) //求字符串长度的函数 int my_strlen(const char ...

  6. [洛谷P1420]最长连号

    题目大意:输入$n$个正整数,($1\leq n\leq 10000$),要求输出最长的连号的长度.(连号指从小到大连续自然数) 题解:考虑从小到大连续自然数差分为$1$,所以可以把原数列差分(后缀自 ...

  7. React context基本用法

    React的context就是一个全局变量,可以从根组件跨级别在React的组件中传递.React context的API有两个版本,React16.x之前的是老版本的context,之后的是新版本的 ...

  8. 免费的dns服务器(更换dns服务器有时可以解决某些网站(如爱奇艺访问不了的问题))

    首先百度提供的dns就是非常好用的dns,小编就把百度的dns作为首选180.76.76.76.   其次是阿里提供的dns223.5.5.5,响应速度非常的快,而且没有广告劫持.   再就是最通用的 ...

  9. HLPP

    LOJ 最大流加强版 #include <bits/stdc++.h> const int inf=0x7fffffff; const int maxn=1210; const int m ...

  10. spring 配置文件读取 mysql username报错

    在配置项目中,spring读取jdbc.properties文件连接mysql时报错:    java.sql.SQLException: Access denied for user 'Admini ...