Delphi 中的字符串的使用

心血来潮,仔细分析比较了一下 Delphi 字符串的各种陷阱。

字符串的声明

可以用以下三种方式来声明字符串:

字符串定义方式 #1

定义方式:S: string;
初始化:S := ‘Hello World’;
说明:这种情况是编译器默认处理的情况,编译器初始化了字符串 S,分配内存调整引用计数,其中 S[0] 为字符串长度,但是不能直接访问,必须使用 Length 或者 SetLength 函数来访问。

字符串定义方式 #2

定义方式:S: PChar;
初始化:S := ‘Hello World’;
说明:这种情况下,实际上编辑器先做了字符串的初始化,再将字符串赋值给字符指针。Delphi 编译器自动处理 PChar 和 String 的转换过程。

字符串定义方式 #3

定义方式:S: array [1..256] of Char;
初始化: S := ‘Hello World’;
说明:这种情况下,对字符串的初始化长度必须小于给定的数组长度,否则默认情况下编译出错。

另外,不能使用的是 s: array of Char; 这种方式的定义,这种方式定义了一个动态数组必须使用 SetLength 来初始化长度,但仍然不能直接对其进行字符串赋值,只能是当作一个数组来使用了。

使用 FillChar 字符串和数组的区别

对于字符串的 FillChar 初始化:

const Len = 10;
var S: string;
begin
  SetLength(S, Len);
  FillChar(S[1], Len, 0);
end;

字符串的 FillChar 调用的第一个参数必须是符串的第一个字符,下标是从 1 开始的。 注意,S[0] 是不允许访问的。也可以使用 PChar 来做这件事情:

var
  S: String;
  P: PChar;
begin
  SetLength(S, 5);
  P := PChar(S);
  FillChar(P[0], 5, 0);
end;

此时,对 P 进行 FillChar 操作时,实际上时按照数组方式来进行,在进行 PChar 转换后, P[0] = S[1] ,因此 P 的下标为 0 。

S := 'Hello';
P := PChar(S);

则: S[1] = P[0] = ‘H’

PChar 和 String 的通用

procedure TForm1.Button1Click(Sender: TObject);
var
  S: string;
  P: PChar;
begin
  S := 'Hello World';
  P := PChar(S);
  ShowMessage(P);
end; 

即:PChar 可以直接当作字符串来使用。连 S := S + P 这样的式子都没有错。

PChar 的使用

S := 'Hello';
P := PChar(S);

实际上 P 此时理论上(使用字符串常数初始化时实际上不是这样)
指向 S[1],需要特别注意 的是,PChar 是指向字符的指针,因此又存在
如下关系:

P[0] = S[1] = P^ = 'H'

P^ 指的是指针指向的内容。同样也存在这样的关系:

Integer(P) = Integer(@p[0])

当使用 SetLength(S, 10) 来初始化 S 时,下列关系成立:

Integer(P) = Integer(@p[0]) = Integer(@S[1])

但是使用字符串常数初始化时实际上不是这样:

Integer(P) = Integer(@p[0]) != Integer(@S[1])

但是 S[1] 的地址却和 P 的地址不同,是否 S := ‘Hello’ 时,S 被复制了?这儿又是一个编译器魔法)
 

PChar 和 PShortString

PChar 的误区

由于 PChar 往往能够和字符串通用,例如:

P: PChar;
P := PChar(S);
ShowMessage(P)

因此,往往会误理解为 PChar 就是字符串,所以在获取字符时

会使用错误用法:

P^[0] (错误用法)

正确的用法是:

P[0]

P 实际上是指向了第一个字符的指针。

PShortString 的用法

对于 PShortString ,它是一个指向字符串的指针。因此,如果

PStr: PShortString;

则 PStr^ 指的是一个字符串,因此获取字符串的内部字符时使用

PStr^[0] (合法形式,但需要注意此时下标从 0 开始)

这里使用 PShortString 又有一个很奇怪的场景:

PStr: PShortString;
S: string;
PStr := PShortString(S); // (正确用法)
// PStr := @S; // (错误用法)

显然,这个用法相当让人费解,不禁让人怀疑其实 String 内部就是一个指针(编译器魔法)错误用法 PStr := @S 合乎对指针的理解: PStr 是指向字 符串的指针,那么现在就取字符串的地址并赋值给它。但是事实上却不管用。正确用法: PStr := PShortString(S); 包括 P := PChar(S) 都显然不 符合指针的定义的。
此处可见 Delphi 本身语法的不统一性。

后记

本人接触 Delphi 已经不下 5 年,但平时若不仔细推敲 Delphi 语法,仍然会深陷其编译器陷阱中,其实很惭愧,本人以前太浮躁。从某种意义上讲,Delphi 在快速开发的同时隐藏了太多的细节,但这些细节又隐藏的不太好,时常又会暴露给程序员。此处涉及到的字符串相关的类型还比较少,除了上述类型外,Delphi 中的字符串还包括 Ansi 和 Wide 系列。

4,000 次阅读

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注