Delphi 中的字符串——《Delphi6 开发人员指南》读书笔记

原创 2003年12月19日 23:15:00

Delphi 中的字符串

——《Delphi6 开发人员指南》读书笔记

Spacesoft【暗夜狂沙】

Delphi 对字符串这个结构的支持是十分丰富的,不仅有Delphi 本身支持的string 类型,还支持和C 语言兼容的字符串数组。那么他们之间有什么区别呢?本文试图就此做一个详细的剖析,并且试图回答论坛上常见的几个问题。

首先,我们要讨论的是Delphi 中的string 到底是什么东东,因为我们在OP 的语法参考手册上知道,Delphi 有三种string : ShortString、AnsiString (就是兼容BCB 的AnsiString 的那种)以及WideString (用来支持Unicode 的,其他的和AnsiString 是完全一样的)。在默认的情况下,编译器将把string 解释为AnsiString ,而需要的时候,可以设置使编译器将string 解释为ShortString(当然,我们很少见到人要这么做,因为AnsiString 用得很好,而ShortString 只支持255 个字符,并且和C 语言的字串数组不兼容,这个我们后面会提到),对应的编译器指示符号是默认的{$H+} 和 {$H-}。

那么,这三种string 到底有什么差别呢?差别在于他们的不同结构。

对于ShortString 来说,它在内存中的结构可以表示为:

strShort = record
    wLength: WORD;
    szBuf: array of Char;
end;

也就是说,对ShortString 的每次操作,不需要遍历这个字符串便可以取得这个字串的大小了。另外,这个字串数组的最后是没有一个#0 字符作为结尾的。因此ShortString 的数组和C 语言的字符串是不兼容的。


对于AnsiString 来说,根据《Delphi6 开发人员指南》的描述,它的结构可以表示如下:

strAnsi = record
   nSize: Integer;
   nRef: Integer;  
   nLength: Integer;
   szBuf: array of Char;
end;

也就是说AnsiString 不仅仅保存了本字串的长度,还保存了对这个字符串的引用数。因此,不同的AnsiString 事实上可能具有相同的物理地址,所以Delphi 的字符串拷贝经常是效率惊人的。然而,当一个字符串改变时怎么办呢?Delphi 将释放对这个字符串的引用(把nRefCount 减1),然后新建一个字符串来装载新的内容。

另外,与ShortString 不同的是,AnsiString 的字符数组是以#0 结尾的,这样szBuf 就可以被当成C 中的字符数组了。估计这样的设计是为了兼容Win32 API。

WideString 和AnsiString 基本上是一样的,所以我们讨论的时候,仅仅讨论AnsiString 和ShortString。

然后, 我们要说明的是,string 具有生存期管理特性。就是说,当string 超出作用域后,字符串占用的资源自动被释放掉。这个机制是怎么实现的呢?对于全局变量,当然它的作用域就是它所在的Unit 的生存周期,那么当然可以在finalization 段进行释放了。而函数中的变量呢?编译器就自动在整个函数的外面套上一个try ... finally 处理。这样,不管什么情况下,这些变量就总能释放掉了。

下面一个问题是:假如现在有一个指针,它被指向一个string, 那么它指向的东西是什么呢?

答案是:指向szBuf 的开头,并且ShortString 不支持这样的强制转换(这是很自然的,因为做这样转化的目的纯粹就是为了和C 语言兼容)。于是string[1] 就是这个字符串的第一个字符。于是,“看起来”这个字符串就只有那些字符了。于是,我们就可以用PChar() 强制类型转化把一个string 转化为一个PChar,然后传到需要字符数组作为参数的Win32 API 中,因为“看起来” 这个字符串这个时候确实是一个标准的C 语言字符串。甚至,我们可以这样使用字符串:

procedure Test();
var
    strBuf: string;
begin
    SetLength(strBuf, 255);  //千万不要忘了先给你的字符串申请足够的空间,不然……嘿嘿,等着弹框儿吧^_^
    GetModuleFileName(0, PChar(strBuf), 255);
    ShowMessage(strBuf);
end;

我们知道,这个API 的原型是这样的:

DWORD GetModuleFileName(
  HMODULE hModule,    // handle to module
  LPTSTR lpFilename,  // Pointer to a buffer that receives the fully-qualified path for the module
  DWORD nSize         // size of buffer
);

这个API居然以为我们真的根据它的要求传了一个array of Char 进去!

通过类似这样的机制,Delphi 使它的使用者在C 语言写的API 原型中可以自由的穿行,一点都没有“二等公民”的感觉,而且又可以享用Delphi 本身的数据类型带来的方便,了不起!

现在我们来总结一下Delphi 中字串类型的特点:

1、与C 语言不同,不是依靠#0 字符做字串结束标志,而是通过字串前面的数字来记录字符串的长度(尽管为了和C 语言兼容,AnsiString 的字串实现确实是在结尾放了一个#0 字符的)
2、string 是具有生存期管理特性的,当string 超出作用域后,字符串占用的资源自动被释放掉。
3、对字符串进行强制类型转化为PChar ,会得到一个字符串指针,这个字串指针的用法和C 语言中的数组是一样的。
对于
    strBuf: string;

    SetLength(strBuf, 255);
你在使用时,可以把PChar(strBuf) 看作是C 语言里的这样一个东西:

    char strBuf[255];

记住,在上个声明里strBuf 是一个指针,它指向那个指针数组的第一个字符的地址,而你做PChar(strBuf) 运算得来的指针就是这个东西。

在最后,解释一下《开发人员指南》里面引起过一段争论的一句话:

“在练习将一个字符串转换为PChar类型时要小心,因为字符串在超出其作用范围时有自动回收的功能,因此当进行P:=PChar(Str)的赋值时,P的作用域(生存期)要比Str 长。”

这句话的意思是这样的:字符串类型是具有自动回收功能的,但是字符串指针没有。P:=PChar(Str)返回的指针可能在作用域之外使用,因此P 的生存周期可能比Str 要长。举个例子来说明:


procedure getPChar(P: PChar);
var
    strTmp: string;
begin
    strTmp := 'asasass';
    P := PChar(strTmp);
end;

这个时候,明显返回的这个指针P 的作用域要大于strTmp,那么这个过程结束的时候,strTmp已经被自动释放掉了。调用这个过程的函数得到的P 事实上已经是一个悬挂指针,没有意义了。作者的本意是提醒读者注意防止这样的情况发生。

参考文献:

1、《Delphi6 开发人员指南》,Steve Teixeira 等著,龙劲松等译,机械工业出版社,北京,2003年1月第一版

2、《Object Pascal 语言参考手册》

欢迎光临作者的个人主页:http://www.mrspace.net/

电子书下载:Delphi 6 开发人员指南

下载:http://www.400gb.com/file/30502619
  • MaxWoods
  • MaxWoods
  • 2013年09月25日 14:04
  • 1326

帖]delphi5开发人员指南学习笔记(1)

  [转帖]delphi5开发人员指南学习笔记(1) 我学delphi断断续续有两年时间了,中间曾头...
  • cg2680139
  • cg2680139
  • 2007年06月12日 00:12
  • 2052

delphi5开发人员指南-读书笔记

以下是若干小收获********************************************************************************************...
  • lee576
  • lee576
  • 2006年06月05日 20:48
  • 1169

003_《Delphi6开发人员指南》

本书是讲述Delphi最经典的著作之一,作为Delphi的新版本,Delphi 6不仅能帮助程序员高效开发Windows应用程序,简化Web服务、中间软件以及后台数据库系统的合成,还是目前惟一全面支持...
  • shuaihj
  • shuaihj
  • 2010年11月18日 08:42
  • 3423

一个使用秘钥加/解密字符串的delphi函数

字符串 秘钥 加密
  • risesoft2012
  • risesoft2012
  • 2016年04月16日 11:46
  • 950

Delphi数据压缩处理(1)

 Delphi数据压缩处理(1)    Borland公司推出的 RAD开发工具 Delphi 5.0作为 Windows平台上的主流开发工具,其可视化的开发环境和面向对象编程的强大功能已经吸引了无数...
  • zou5655
  • zou5655
  • 2001年09月29日 15:10
  • 796

关于在DELPHI6中使用正则表达式的一些心得

一. 工具选择DELPHI6本身没有处理正则表达式的库,只能找第三方库。在选择方面,我的标准是:1. 不必向开发环境注册控件2. 接口简单3. 符合主流的perl式正则语法4. 专业,至少应该有个看上...
  • johnnyjian
  • johnnyjian
  • 2007年04月01日 14:55
  • 1080

Delphi6控件内容完整截屏

由于用户那套旧系统需要截取Form中控件的内容,但经常因内容超长而截屏不完整(又被踢下坑了)。 经搜索,网上有不少关于这方面的资料(感谢他们的分享),再自己琢磨了一下,得到了以下代码。 值得注意的是,...
  • zhaogang
  • zhaogang
  • 2016年12月13日 15:59
  • 604

Delphi6项目迁移到Delph7时最常见问题

 一个遗留的老项目,原来是在Delphi 6(D6)环境下开发和编译的。这两天计划将其迁移到Delphi 7(D7)下,结果使用D7编译后的新程序不能正常运行,出现某些界面打不开的情况。经跟踪测试发现...
  • tjianliang
  • tjianliang
  • 2008年11月21日 17:28
  • 1402

Delphi6 反复提示要注册的问题解决啦

好一段时间没有用 Delphi6了,今天在打开一个工程的时候竟然反复提示要注册。就算是用Kg提供的SN与激活方法都失败了,除了提示注册失败还是失败,郁闷。。。折腾了半天,后面找到注册表:[HKEY_C...
  • tanaya
  • tanaya
  • 2009年08月13日 17:22
  • 3309
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Delphi 中的字符串——《Delphi6 开发人员指南》读书笔记
举报原因:
原因补充:

(最多只允许输入30个字)