我所理解的Delphi中的数组类型

动态数组的使用
//例1:
var
StrArr: array of String; //动态数组定义时不与维数
begin
SetLength(StrArr,6,’,’,’); //分配6个元素位置: 0-5
StrArr[0] := ‘万一’; //动态数组的下界是 0
ShowMessage(StrArr[0],’,’,’); //分配空间后和静态数组一样使用
StrArr := nil; //一般没必要手动释放, 动态数组离开作用域会自释放

数组可以使Object
Pascal所拥有的任何数据类型,数组是一些数值的简单集合。
 var
  MyArray: array[0..4] of Integer;        {
声明一个数组包括5个整数数值}
begin
  MyArray[0] := -200;                     {
通过操作符[]就可以访问每个数组元素}
  MyArray[1] := -100;
  MyArray[2] := 0;
  MyArray[3] := 100;
  MyArray[4] := 200;
  MyArray[0] := MyArray[1] + MyArray[4];  { MyArray[0]为-100}
end;
其MyArray在内存空间的分布,每个整数需要4个字节,因此整个数组将占20个字节的内存,如下:

Delphi有三种类型的字符:

end;

//例2. 动态数组的引用:
var
Arr1,Arr2: array of Integer;
a: array[0..1] of Integer;
begin
SetLength(Arr1,6,’,’,’);
Arr1[5] := 100;

Arr2 := Arr1; //Arr2 引用了 Arr1
ShowMessage(IntToStr(Arr2[5]),’,’,’); //100
ShowMessage(IntToStr(Length(Arr2)),’,’,’); //当然 Arr2 维数也会是 6

ShowMessage(IntToStr(SizeOf(Arr1)),’,’,’); //4, 其实动态数组是个指针
ShowMessage(IntToStr(SizeOf(Arr2)),’,’,’); //4

Arr2[5] := 99; //现在它们指向同一个数组, 改变这个就是改变那个
ShowMessage(IntToStr(Arr1[5]),’,’,’); //99

Arr1 := nil; //释放其中一个指针, 数组继续存在
ShowMessage(IntToStr(Arr2[5]),’,’,’); //99

澳门新葡萄京官网首页 1

AnsiChar这是标准的1字节的ANSI字符,程序员都对它比较熟悉。

end;

//例3. 数组 Copy <1>:
var
Arr1,Arr2: array of Integer;
begin
SetLength(Arr1,6,’,’,’);
Arr1[5] := 100;

Arr2 := Copy(Arr1,’,’,’); //数组 Copy
Arr2[5] := 99; //改变 Arr2 不再影响 Arr1
ShowMessage(IntToStr(Arr1[5]-Arr2[5]),’,’,’); //1

SetLength(Arr1,7,’,’,’);
ShowMessage(IntToStr(Length(Arr1)),’,’,’); //7
ShowMessage(IntToStr(Length(Arr2)),’,’,’); //6, 没有一点牵扯了

1、多维数组
 const
  CArray: array[0..4] of Integer = (-20, -100, 0, 100, 200);
  { 数组常量的在声明的同时也要进行赋初值}
var
  MyArray: array[0..2, 0..4] of Integer; { 两维数组的声明}
  UArray: array[10..20] of Integer;      {
声明了一个下界10到上界20的10个元素的数组}
  X: Integer;
begin                                    { 两种方法可以访问两维数组}
  X := MyArray[1][1] + MyArray[2][1];    { 1、[X][Y]访问}
  X := MyArray[1, 1] + MyArray[2, 1];    { 2、[X, Y]访问}
 
  { 下面的访问超出了数组范围,
  将会提示“Constant expression violates subrange bounds”错误}
  X := MyArray[3, 1] + MyArray[0, 5];   
{注意这两个元素超出数组声明的范围}
end;
其中MyArray被声明为一个二维数组,其在内存中的分布如下:
2、上界与下界
处理数组会经常用到上界(Low)和下界(High)函数。如:
 var
  X, I, Lower, Upper: Integer;
  MyArray: array[10..20] of Integer;
begin
  { 这里假使已经将MyArray数组进行了赋初值}
  Lower := Low(MyArray); { Lower的值为 10}
  Upper := High(MyArray);{ Upper的值为 20}
  X := 0;
  for I := Lower to Upper do
  begin
澳门新葡萄京官网首页,    X := X + MyArray[I]; { X将累加数组元素值的和}
  end;
end;
使用上下界函数可以保证存取数组时不越界。
对了,如果换成二维数组,上下界函数如何用呢???
 var
  Lower1, Upper1: Integer;{ 用来存储一维的上下界}
  Lower2, Upper2: Integer;{ 用来存储二维的上下界}
  MyArray: array[10..20, 1..2] of Integer;
begin
  { 这里假使已经将MyArray数组进行了赋初值}
  Lower1 := Low(MyArray); { Lower的值为 10}
  Upper1 := High(MyArray);{ Upper的值为 20}
  ShowMessage(‘第一维的下界为 ‘ + IntToStr(Lower1) +
              ‘,上界为 ‘ + IntToStr(Upper1));
  Lower2 := Low(MyArray[Lower1]); {获取MyArray[10]下界}
  Upper2 := High(MyArray[Lower1]);{获取MyArray[10]上界}
  ShowMessage(‘第二维的下界为 ‘ + IntToStr(Lower2) +
              ‘,上界为 ‘ + IntToStr(Upper2));
end;
两次消息框显示界面如下:

WideChar这是2字节的Unicode字符。

end;

//例4. 数组 Copy <2>:
var
Arr1,Arr2: array of Integer;
i: Integer;
begin
SetLength(Arr1,6,’,’,’);
for i := Low(Arr1) to High(Arr1) do //给每个元素赋值
Arr1[i] := i+1;

Arr2 := Copy(Arr1,1,3,’,’,’); //只 Copy 第2..4个元素

ShowMessage(IntToStr(Arr1[1]),’,’,’); //2, 现在 Arr2[0] 和 Arr1[1]
的值是一样的
ShowMessage(IntToStr(Arr2[0]),’,’,’); //2

ShowMessage(IntToStr(Length(Arr1)),’,’,’); //6, 维数肯定不一样了
ShowMessage(IntToStr(Length(Arr2)),’,’,’); //3

  

Char在目前相当于AnsiChar,但在Delphi 2010 以后版本中相当于WideChar.

end;

//例5. 动态多维数组:
var
Arr: array of array of Integer; //定义多维数组
begin
SetLength(Arr,5,5,’,’,’); //分配空间
Arr[0,3] := 100; //赋值
ShowMessage(IntToStr(Arr[0,3]),’,’,’); //取值

澳门新葡萄京官网首页 2  
澳门新葡萄京官网首页 3

记住因为一个字符在长度上并不表示一个字节,所以不能在应用程序中对字符长度进行硬编码,

end;

//例6. 另类建立:
var
Arr: array of Integer;
begin
Arr := varArrayCreate([0,3],varInteger,’,’,’);
ShowMessage(IntToStr(Length(Arr)),’,’,’); //4

Arr := VarArrayOf([1,2,3,4],’,’,’);

ShowMessage(IntToStr(Arr[0]),’,’,’); //1
{这是给变体数组使用的,可能会有效率问题}
end;

 3、动态数组(dynamic array)
动态数组是一种在运行时分配内存的数组,一个动态数组可以变大,也可以变小。
声明一个动态数组,只要在声明时不要制定维数,就像这样:
 var
  SA: array of string;                 { 一维动态数组}
begin
  { 使用SetLength进行动态数组的空间分配,已有元素可以得到保留}
  SetLength(SA, 3);
  SA[0] := ‘Hello World’;
  { 重新分配了动态数组大小为2个元素}
  SetLength(SA, 2);
  ShowMessage(SA[0]);  {显示为’Hello World’,说明已有元素得到保留}
end;
用同样的方法也可以建立二维动态数组,如下:
 var
  SA: array of array of string;   { 二维动态数组}
begin
  { 使用SetLength进行动态数组的空间分配,已有元素可以得到保留}
  SetLength(SA, 20, 20);
  SA[0][0] := ‘Hello World’;
  { 重新分配了动态数组大小为2个元素}
  SetLength(SA, 10, 10);
  ShowMessage(SA[0][0]);  {显示为’Hello
World’,说明已有元素得到保留}
end;
动态数组建立后就可以像普通数组一样使用。
动态数组通常都是以0为基准的。
动态数组是生存期自管理的,使用完它们后没有必要释放,离开作用域后它们会被自动释放。当然,如果你想在离开作用域前就删除动态数组(比如它占用太多的内存了,需要释放掉),那就用下面的语句就可以了。
 var
  SA: array of string;
begin
  SetLength(SA, 20);
  SA[0] := ‘Hello World’;
  SA := nil; { 直接把nil赋值给SA就可以了}
end;
动态数组的复制问题
 var
  A1, A2: array of Integer;
begin
  SetLength(A1, 4);
  A2 := A1;
  A1[0] := 1;
  A2[0] := 26;
  ShowMessage(IntToStr(A1[0])); { 显示结果为 26}
  {
   为什么显示26呢?因为A2 :=
A1这条赋值语句,并没有创建新的数组,仅仅只是将
  
A1数组的引用赋值给了A2,也就是说A2只是A1的一个别名而已,指向的都是相同东西,
  
因此对A2所做的任何操作都会影响到A1,如果想要完全复制创建一个新的A2数组需要
   用到Copy函数,如下:
  }
 
  A2 := Copy(A1); { A1, A2现在为两个独立的数组}
  A2[0] := 10;
  A1[0] := 26;
  ShowMessage(IntToStr(A2[0]));  { 现在A2的值为10,说明完全的独立了}
  A2 := nil; {释放掉A2}
 
  A2 := Copy(A1, 1, 2); { 从元素1开始,复制2个元素到A2}
end;
以上都是在Delphi7环境中测试通过

而应该使用Sizeof()函数。注意Sizeof()标准函数返回类型或实例的字节长度。

 摘自 瓢虫Monster
 

Delphi有下列几种不同的字符串类型 String:

Pascal所拥有的任何数据类型,数组是一些数值的简单集合。 var MyArray:
array[0..4] of Integer; { 声明一个数组包括5个整数数值…

ShortString保留该类型是为了向后兼容
Delphi1.0,它的长度限制在255个字符内。 
ShortString[0] = len : $H- 代表 ShortString

AnsiString是Pascal缺省的字符串类型,它由AnsiChar字符组成,其长度没有限制,
同时与null结束的字符串相兼容。<Delphi2.0开始引入> : $H+ 代表
AnsiString

WideString功能上类似于AnsiString,但它是由WideChar字符组成的。

WideString没有引用计数,所以将一个WideString字符串赋值给另一个WideString字符串时,
就需要从内存中的一个位置复制到另一个位置。这使得WideString在速度和内存的利用上不如AnsiString有效。

缺省情况下,编译器认为是AnsiString字符串<
<Delphi2010 String 默认为 WideString>

Delphi有下列几种不同的字符串指针类型:

PChar指向null结束的Char字符串的指针,类似于C的char*或lpstr类型。

PAnsiChar指向null结束的AnsiChar字符串的指针。

PWideChar指向null结束的WideChar字符串的指针。<Delphi2010 PChar 默认为 PWideChar>

字符数组

静态字符数组 CharArray : array[0..Length-1] of Char;

动态字符数组 CharArray : array of Char;

动态数组的构造和静态数组完全不同, 它和 String 的构造相似

String  : RefCount : Length : Char Char …. 0x00 < Delphi >

DynArry : RefCount : Length : Char Char …. 0x00 < Programmer >

StaArry :                   : Char Char …. 0x00 < Programmer >

 

                           : PChar = PChar( String ) = PChar( DynArray )
= PChar( StaArray )

StrLen( PChar )  : 字符个数, StrLen( PChar ) * SizeOf( Char ) :
字节个数 ( WideChar = 2 )

Length( String ) : 字符个数, Length( PChar ) * SizeOf( Char ) :
字节个数 ( AnsiChar = 1 )

 

1.AnsiString类型

AnsiString(或长字符串)类型是在Delphi2.0开始引入的,因为Delphi1.0的用户特别需要一个容易使用而且没有255个字符限制的字符串类型,而AnsiString正好能满足这些要求。

虽然AnsiString在外表上跟以前的字符串类型几乎相同,但它是动态分配的并有自动回收功能,正是因为这个功能AnsiString有时被称为生存期自管理类型。ObjectPascal能根据需要为字符串分配空间,所以不用像在C/C++中所担心的为中间结果分配缓冲区。另外,AnsiString字符串总是以null字符结束的,这使得AnsiString字符串能与Win32API中的字符串兼容。实际上,AnsiString类型是一个指向在堆栈中的字符串结构的指针。

AnsiString字符串类型有引用计数的功能,这表示几个字符串都能指向相同的物理地址。因此,复制字符串因为仅仅是复制了指针而不是复制实际的字符串而变得非常快。当两个或更多的AnsiString类型共享一个指向相同物理地址的引用时,Delphi内存管理使用了copy-on-write技术,一个字符串要等到修改结束,才释放一个引用并分配一个物理字符串。下面的例子显示了这些概念:

var

S1,S2:string;

begin

//给S1赋值,S1的引用计数为1

S1:=’Andnowforsomething…’;

S2:=S1;//现在S2与S1指向同一个字符串,S1的引用计数为2

//S2现在改变了,所以它被复制到自己的物理空间,并且S1的引用计数减1

S2:=S2+’completelydifferent1′;

end;

Win32的兼容

正如前面所提到,AnsiString字符串总是null结束的。因此,它能跟以null结尾的字符串兼容,这就使得调用Win32API函数或其他需要PChar型字符串的函数变得容易了。只要把一个字符类型强制转换为PChar类型(在2.8节“强制类型转换和类型约定”中将介绍强制类型转换)。下面的代码演示了怎样调用Win32的GetWindowsDirectory()函数,这个函数需要一个PChar类型的参数:

var

S:String;

begin

SetLength(S,256);//重要!首先给字符串分配空间

//调用API函数,S现在包含目录字符串

GetWindowsDirectory(PChar(S),256);

如果使用了将AnsiString字符串强制转换为PChar类型的函数和过程,在使用结束后,要手工把它的长度恢复为原来以null结束的长度。STRUTILS单元中的RealizeLenght()函数可以实现这一点:

procedureRealizeLength(varS:string);

begin

SetLength(S,StrLen(PChar(S)));

end;

调用ReallizeLength():

var

S:string;

begin

SetLength(S,256);//重要!首先给字符串分配空间

//调用函数,S现在包含目录字符串

GetWindowDirectory(PChar(S),256);

RealizeLength(S);//设置S的长度为null结束的长度

end;

跟AnsiString类型字符串不一样,ShortString跟以null结尾的字符串不兼容,正因为这样,用ShortString调用Win32函数时,要做一些工作。下面这个ShortStringAsPChar()函数是在STRUTILS.PAS单元中定义的。

funcfunctionShortStringAsPChar(varS:ShortString):PChar;

{这函数能使一个字符串以null结尾,这样就能传递给需要PChar类型参数的Win32API函数,如果字符串超过254个字符,多出的部分将被截掉}

begin

ifLength(S)=High(S)thenDec(S[0]);{如果S太长,就截取一部分}

S[Ord(Length(S))+1]:=#0;{把null加到字符串的最后}

Result:=@S[1];{返回PChar化的字符串}

end;

Win32API函数需要以null结尾的字符串,不要把ShortString字符串传递给API函数,因为编译器将报错,长字符串可以传递给Win32API函数。

WideString类型

WideString类型像AnsiString一样是生存期自管理类型,它们都能动态分配、自动回收并且彼此能相互兼容,不过WideString和AnsiString的不同主要在三个方面:

WideString由WideChar字符组成,而不是由AnsiChar字符组成的,它们跟Unicode字符串兼容。

WideString用SysAllocStrLen()API函数进行分配,它们跟OLE的BSTR字符串相兼容。

WideString没有引用计数,所以将一个WideString字符串赋值给另一个WideString字符串时,就需要从内存中的一个位置复制到另一个位置。这使得WideString在速度和内存的利用上不如AnsiString有效。

就像上面所提到的,编译器自动在AnsiString类型和WideString类型的变量间进行转换。示例如下:

var

W:wideString;

S:string;

begin

W:=’Margaritaville’;

S:=W;//wideString转换成AnsiString

S:=’ComeMonday’;

W:=S;//AnsiString转换成WideString

end;

为了能灵活地运用WideString类型,ObjectPascal重载了Concat()、Copy、Insert()、Length()、Pos()和SetLength()等例程以及+、=和<>等运算符。

就像AnsiString和ShortString类型一样,能用数组的下标来访问WideString中一个特定的字符:

var

W:WideString;

C:WideChar;

begin

W:=’EbonyandIvorylivinginprefectharmony’;

C:=W[Length(W)];//C包含W字符串的最后一个字符

end;

以null结束的字符串

正如前面所提到的,Delphi有三种不同的以null结束的字符串类型:PChar、PAnsiChar和PWideChar。它们都是由Delphi的三种不同字符组成的。这三种类型在总体上跟PChar是一致的。PChar之所以保留是为了跟Delphi1.0和Win32API兼容,而它们需要使用以null结束的字符串,PChar被定义成一个指向以null(零)结束的字符串指针与AnsiString和WideString类型不同,PChar的内存不是由ObjectPascal自动产生和管理的,要用Object
Pascal的内存管理函数来为PChar所指向的内存进行分配。PChar字符串的理论最大长度是4GB

在大多数情况下,AnsiString类型能被用成PChar,应该尽可能地使用AnsiString,因为它对字符串内存的管理是自动,极大地减少了应用程序中内存混乱的错误代码,因此,要尽可能地避免用PChar类型以及对它相应进行人工分配内存。

正如在前面所提到的,PChar变量需要人工分配和释放存放字符串的内存。通常,用StrAlloc()函数为PChar缓冲区分配内存,但是其他几种函数也能用来为PChar类型分配函数,包括AllocMem()、GetMem()、StrNew()和VirtualAlloc()API函数。这些函数有相应的释放内存的函数。


//单字符 Char、AnsiChar (在目前版本(2007)中, 它们是一回事, 只有 1
字节大小)
var
c: Char; {Char 类型的取值范围是: #0..#255, 用十六进制表示是:
#$0..#$FF}
begin
{用十进制方式赋值:}
c := #65;
ShowMessage(c); {A}

{用十六进制方式赋值:}
c := #$41;
ShowMessage(c); {A}

{用 Chr 函数代替 # 符号}
c := Chr(65);
ShowMessage(c); {A}
c := Chr($41);
ShowMessage(c); {A}

{Char 长度当然会是 1}
ShowMessage(IntToStr(Length(c))); {1}

{Char、AnsiChar 允许这样方便地赋值(也就是和 1
字节长度的字符串是兼容的):}
c := ‘B’;
ShowMessage(c); {B}

end;

//UniCode 字符 WideChar; 和 AnsiChar 不同, WideChar 是占 2 字节大小.
var
c: WideChar; {WideChar 的取值范围是: #0..#65535, 用十六进制表示是:
#$0..#$FFFF}
begin
{WideChar 兼容了 AnsiChar 的 #0..#255; 但占用了 2 字节大小}
c := #65;
ShowMessage(c); {A}
ShowMessage(IntToStr(Length(c))); {1; 这是字符长度}
ShowMessage(IntToStr(SizeOf(c))); {2; 但占用 2 个字节}

{用十六进制赋值}
c := #$4E07;
ShowMessage(c); {万}
ShowMessage(IntToStr(Length(c))); {1; 这是字符长度}
ShowMessage(IntToStr(SizeOf(c))); {2; 但占用 2 个字节}

{用十进制赋值}
c := #19975;
ShowMessage(c); {万}

{如果不超出 #255 的范围是可以直接赋值的}
c := ‘B’;
ShowMessage(c); {万}

{这样不行}
//c := ‘万’; {这是 Delphi 的支持问题, 估计 Delphi 2008 应该可以解决}

{可以这样变通一下:}
c := WideString(‘万’)[1];
ShowMessage(c); {万}

{用 WideChar 的方式显示我的名字}
ShowMessage(#19975#19968);    {万一}
ShowMessage(#19975 + #19968); {万一}
ShowMessage(#$4e07#$4e00);    {万一}

end;

//字符指针 PChar、PAnsiChar; 在当前版本(2007)中它们没有区别.
var
p: PChar;
str: string;
begin
{可以给 PChar 直接赋予字符串常量}
p := ‘万一’;
ShowMessage(p);                   {万一}
ShowMessage(IntToStr(Length(p))); {4}

{给变量值需要转换}
str := ‘万一的
Delphi 博客’;
p := PChar(str); {转换}
ShowMessage(p);                   {万一的 Delphi 博客}
ShowMessage(IntToStr(Length(p))); {18}

end;

//宽字符指针 PWideChar
var
p: PWideChar;
str: WideString; {注意这里不是 String}
begin
{可以给 PWideChar 直接赋予字符串常量}
p := ‘万一’;
ShowMessage(p);                   {万一}
ShowMessage(IntToStr(Length(p))); {2}

{给变量值需要转换}
str := ‘万一的 Delphi 博客’;
p := PWideChar(str); {转换}
ShowMessage(p);                   {万一的 Delphi 博客}
ShowMessage(IntToStr(Length(p))); {13}
end;

详见:

        

         

发表评论

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