Delphi 中的字符串的使用

心血来潮,仔细分析比较了一下 Delphi 字符串的各种陷阱。
 
1. 字符串的声明:
 
    可以用以下三种方式来声明字符串:
 
    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 来初始化长度,但仍然不能直接对其进行字符串赋值,只能是当作一个数组来使用了。
 
2. 使用 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’
3. 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 这样的式子都没有错。
 
4. 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 被复制了?
   (这儿又是一个编译器魔法)
 
5. PChar 和 PShortString
 
    1) PChar 的误区:
 
    由于 PChar 往往能够和字符串通用,例如:
 
        P: PChar;
       P := PChar(S);
       ShowMessage(P)
  
   因此,往往会误理解为 PChar 就是字符串,所以在获取字符时
    会使用错误用法:
 
        P^[0] (错误用法)
 
    正确的用法是:
 
        P[0]
 
    P 实际上是指向了第一个字符的指针。
 
    2) 看看 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 系列。
751 次阅读

发表评论

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