字符串类型
Delphi有三种类型的字符:
●AnsiChar这是标准的1字节的ANSI字符,程序员都对它比较熟悉。
●WideChar这是2字节的Unicode字符。
●Char在目前Delphi早前的版本相当于AnsiChar,但在Delphi以后版本中相当于WideChar.
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 个字节}
{如果不超出 #255 的范围是可以直接赋值的}
c := 'B';
ShowMessage(c); {万}
end;
记住因为一个字符在长度上并不表示一个字节,所以不能在应用程序中对字符长度进行硬编码,而应该使用Sizeof()函数。注意Sizeof()标准函数返回类型或实例的字节长度。
字符串是代表一组字符的变量类型,Delphi有下列几种不同的字符串类型来满足程序的要求:
●AnsiString这是Pascal缺省的字符串类型(string),它由AnsiChar字符组成,其长度没有限制,同时与null结束的字符串相兼容。
●ShortString保留该类型是为了向后兼容Delphi1.0,它的长度限制在255个字符内。
●WideString功能上类似于AnsiString,但它是由WideChar字符组成的。
●PChar指向null结束的Char字符串的指针,类似于C的char*或lpstr类型。
●PAnsiChar指向null结束的AnsiChar字符串的指针。
●PWideChar指向null结束的WideChar字符串的指针。
1.AnsiString类型
AnsiString(或长字符串)类型是在Delphi2.0开始引入的,因为Delphi1.0的用户特别需要一个容易使用而且没有255个字符限制的字符串类型,而AnsiString正好能满足这些要求。
虽然AnsiString在外表上跟以前的字符串类型几乎相同,但它是动态分配的并有自动回收功能,正是因为这个功能AnsiString有时被称为生存期自管理类型。ObjectPascal能根据需要为字符串分配空间,所以不用像在C/C++中所担心的为中间结果分配缓冲区。实际上,AnsiString类型是一个指向在堆栈中的字符串结构的指针。
AnsiString字符串类型有引用计数的功能,这表示几个字符串都能指向相同的物理地址。因此,复制字符串因为仅仅是复制了指针而不是复制实际的字符串而变得非常快。当两个或更多的AnsiString类型共享一个指向相同物理地址的引用时,Delphi内存管理使用了copy-on-write技术,一个字符串要等到修改结束,才释放一个引用并分配一个物理字符串。当我们声明一个AnsiString变量时,它是没有长度的,因此字符串没有为字符申请内存空间。为了申请字符串的内存空间,我们可以给它赋值为一行字符或者另一个字符串变量,也可以使用SetLength()函数。
var
str: string;
s: string;
begin
str := 'delphi';
s[1] := 'd';//错误,s此时还没有申请内存空间。
s := 'delphi'; //或者s := str; 或者SetLength(s, 4);
end;
AnsiString字符串总是null结束的,这就使得调用Win32API函数或其他需要PChar型字符串的函数变得容易了。只要把一个字符类型强制转换为PChar类型。我们在调用时进行强制类型转换PChar(s),或者@s[1]。(s[1]是字符串s的第一个字符,@是取地址。s[0]存放的是字符串s的长度)。下面的代码演示了怎样调用Win32的GetWindowsDirectory()函数,这个函数需要一个PChar类型的参数:
var
S:String;
begin
SetLength(S,256);//重要!首先给字符串分配空间
//调用API函数,S现在包含目录字符串
GetWindowsDirectory(PChar(S),256);
如果使用了将AnsiString字符串强制转换为PChar类型的函数和过程,在使用结束后,要手工把它的长度恢复为原来以null结束的长度。STRUTILS单元中的RealizeLenght()函数可以实现这一点:
procedure RealizeLength(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;
2. ShortString类型
ShortString类型跟以null结尾的字符串不兼容,正因为这样,用ShortString调用Win32函数时,要做一些工作。下面这个ShortStringAsPChar()函数是在STRUTILS.PAS单元中定义的。
function ShortStringAsPChar(varS:ShortString): PChar;
{这函数能使一个字符串以null结尾,这样就能传递给需要PChar类型参数的Win32API函数,如果字符串超过254个字符,多出的部分将被截掉}
begin
if Length(S)=High(S) then Dec(S[0]); {如果S太长,就截取一部分}
S[Ord(Length(S))+1]:=#0;{把null加到字符串的最后}
Result:=@S[1];{返回PChar化的字符串}
end;
Win32API函数需要以null结尾的字符串,不要把ShortString字符串传递给API函数,因为编译器将报错,长字符串可以传递给Win32API函数。
3.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中一个特定的字符。
4.字符串指针
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类型以及对它相应进行人工分配内存。在Debug下开启内存泄漏:
{$ifdef DEBUG}
ReportMemoryLeaksOnShutDown := True; //插入到应用程序入口的begin语句下。
{$endif}
通常,用StrAlloc()函数为PChar缓冲区分配内存,但是其他几种函数也能用来为PChar类型分配函数,包括AllocMem()、GetMem()、StrNew()和VirtualAlloc()API函数。这些函数有相应的释放内存的函数。
var
p: PChar;
str: string;
begin
{可以给 PChar 直接赋予字符串常量}
p := '中国';
ShowMessage(IntToStr(Length(p))); {4}
{给变量值需要转换}
str := '中国杭州';
p := PChar(str); {转换}
ShowMessage(p); {中国杭州}
ShowMessage(IntToStr(Length(p))); {8}
end;
//宽字符指针 PWideChar
var
p: PWideChar;
str: WideString; {注意这里不是 String}
begin
{可以给 PWideChar 直接赋予字符串常量}
p := '中国';
ShowMessage(IntToStr(Length(p))); {2}
{给变量值需要转换}
str := '中国杭州';
p := PWideChar(str); {转换}
ShowMessage(p); {中国杭州}
ShowMessage(IntToStr(Length(p))); {4}
end;
数值型
整数型(Integer):整数型是整数的集合,包括多种不同的整数类别。
类型 | 表示范围 | 字节 |
ShortInt | -128~127 | 8位,有正负符号标志 |
SmallInt | -32 768~32 767 | 16位,有正负符号标志 |
LongInt | -2 147 443 648~2 147 483 647 | 32位,有正负符号标志 |
Integer | -2 147 443 648~2 147 483 647 | 32位,有正负符号标志 |
Int64 | -263~263-1 | 64位,有正负符号标志 |
Byte | 0~255 | 8位,无正负符号标志 |
Word | 0~65 535 | 16位,无正负符号标志 |
LongWord | 0~4 294 967 295 | 32位,无正负符号标志 |
Cardinal | 0~4 294 967 295 | 32位,无正负符号标志 |
实数型(Real):实数型是实数的集合,包括多种不同的类型。Real类型只有在和以前Borland Pascal兼容的情况下才使用,否则应使用Double或Extended类型。
类型 | 范围 | 有效位 | 存储字节 |
Real48 | 2.9×10-39 ~ 1.7×1038 | 11~12 | 6 |
Single | 1.5×10-45 ~ 3.4×1038 | 7~8 | 4 |
Double | 5.0×10-324 ~ 1.7×10308 | 15~16 | 8 |
Extended | 3.6×10-4951 ~ 1.1×104932 | 19~20 | 10 |
Comp | -263+1 ~ 263-1 | 19~20 | 8 |
Currency | -922 337 203 685 477.5 808 ~ 922 337 203 685 477.5 807 | 19~20 | 8 |
Real | 5.0×10-324 ~ 1.7×10308 | 15~16 | 8 |
Delphi数据类型分类如下:
分类 | 范围 | 字节 | 备注 | |||
简单类型 | 序数 | 整数 | Integer | -2147483648 .. 2147483647 | 4 | 有符号32位 |
Cardinal | 0 .. 4294967295 | 4 | 无符号32位 | |||
Shortint | -128 .. 127 | 1 | 有符号8位 | |||
Smallint | -32768 .. 32767 | 2 | 有符号16位 | |||
Longint | -2147483648 .. 2147483647 | 4 | 有符号32位 | |||
Int64 | -263 .. 263 | 8 | 有符号64位 | |||
Byte | 0 .. 255 | 1 | 无符号8位 | |||
Word | 0 .. 65535 | 2 | 无符号16位 | |||
Longword | 0 .. 4294967295 | 4 | 无符号32位 | |||
字符 | AnsiChar(Char) | ANSI字符集 | 8位 | |||
WideChar | Unicode字符集 | 16位 | ||||
布尔 | Boolean | False < True | 1 | |||
ByteBool | False <> True | 1 | ||||
WordBool | 2 | |||||
LongBool | 4 | |||||
枚举 | ||||||
子界 | ||||||
实数 |
| Real | 5.0×10-324 .. 1.7×10308 | 8 | [精度]15..16 | |
Real48 | 2.9×10-39 .. 1.7×1038 | 6 | [精度]11..12; | |||
Single | 1.5×10-45 .. 3.4×1038 | 4 | [精度]7..8 | |||
Double | 5.0×10-324 .. 1.7×10308 | 8 | [精度]15..16 | |||
Extended | 3.6×10-4951 .. 1.1×104932 | 10 | [精度]19..20 | |||
Comp | -263 + 1 .. 263 - 1 | 8 | [精度]19..20 | |||
Currency | -922337203685477.5808 .. | 8 | [精度]19..20 | |||
字符串 |
|
| ShortString | 255个字符 | 2..256B | 向后兼容 |
AnsiString | 大约 231 个字符 | 4B..2GB | 8位(ANSI)字符 | |||
WideString | 大约 230 个字符 | 4B..2GB | 多用户服务和 | |||
其他 | String | |||||
结构类型 | 集合 |
| Set | 最多256个元素[0..255] | ||
数组 | 静态数组 |
|
| |||
动态数组 |
| |||||
记录 |
| Record | ||||
文件 |
| File | ||||
类 |
| Class | ||||
类引用 |
| Class reference | ||||
接口 |
| Interface | ||||
指针类型 | 无类型指针 |
| Pointer | |||
有类型指针 | 预定义类型指针 | PAnsiString | ||||
过程类型 | 程序过程类型 |
| Procedural | |||
对象过程类型 |
| Procedural |
| |||
变体类型 |
|
| Variant |
| ||
| OleVariant |
|
强制类型转换
一、数的类型转换
把表达式的类型从一种类型转化为另一种类型,结果值是把原始值截断或扩展,符号位保持不变。例如:
数的类型转换
字符转换为整数 Integer('A')
整数转换为字符 Char(48)
整数转换为1个字节的逻辑型 Boolean(0)
整数转换为2个字节的逻辑型 WordBool(0)
整数转换为4个字节的逻辑型 LongBool(0)
整数转换为10进制pascal型字符串 caption:=intToStr(15)
整数转换为16进制pascal型4位字符串 caption:=intToHex(15,4)
地址转换为长整型数 Longint(@Buffer)
二、数的"分开"与"合成"
取32位longint型数的 高16位数为 hiword(longint-var)
低16位数为 loword(longint-var)
取16位数的 高8位数为 hibyte(integer_var)
低8位数为 lobyte(integer_var)
取32位地址的段选择符和偏移量 段选择符(高16位地址)为 selectorof(p)
偏移量(低16位地址)为 offsetof(p)
段选择符和偏移量合成为指针 Ptr(SEG, OFS: Word)相当于C语言的宏MK-FP(SEG,OFS)
例如在Windows中,Task DataBase结构0FAh偏移处包含'TD'标识,我们可以容易地编写如下代码观察到这个位于Windows内部的未公开的秘密:
{函数ptr(seg,ofs)的用法,相当于C语言的MK-FP(seg,ofs)}
var p:pbyte;ch:char;
p:=ptr(getcurrentTask,$FA);
ch:=char(p^); {结果为ch='T'}
p:=ptr(getcurrentTask,$FA+1);
ch:=char(p^); {结果为ch='D'}
三、字符串string 字符数组与指向字符串的指针pchar的区别与联系
1、使用指向字符串的指针,如果不是以0结尾,运行时就会出现错误。为了避免这种错误,需要在字符串结尾人工加入0 即char(0),或用strpcopy函数在字符串结尾自动加0。
例1: 指向字符串的指针,如果不是以0结尾,运行时会出现错误:
{s[0]=3 s[1]='n' s[2]='e' s[3]='w'}
var
s:string;
p:pchar;
begin
s:='new';
label1.caption:=s; {new}
label2.caption:=intTostr(integer(s[0]));{3是字符串的长度}
p:=@s[1];{不是以0结尾,莫用pchar型指针}
label3.caption:=strpas(p); {运行时出现错误}
end;
例2:在字符串结尾人工加入0即char(0),可使用指向字符串的指针。
{s[0]=4 s[1]='n' s[2]='e' s[3]='w' s[4]=0;}
{p-->'new'}
var
s:string;
p:pchar;
begin
p:=@s[1];
s:='new'+char(0); {以0结尾,可用pchar型指针}
label1.caption:=strpas(p); {new}
label2.caption:=s; {new}
label3.caption:=intTostr(integer(s[0])); {4是字符串长度}
end;
例3: 用strpcopy函数赋值会在字符串s结尾自动加0。
{s[0]=4 s[1]='n' s[2]='e' s[3]='w' s[4]=0;}
{p-->'new'}
var
s:string;
p:pchar;
begin
p:=@s[1];
strpcopy(p,'new');{strpcopy函数在字符串结尾自动加0}
label1.caption:=strpas(p);{new}
label2.caption:=s;{new}
label3.caption:=intTostr(integer(s[0]));{4}
end;
2、下标为0的字符串标识符存放的是字符串长度,字符型数组基本相当于字符串,但不能存放字符串长度。字符串可以用s:='a string'的形式赋值,但是字符型数组a[ ]不可直接用a:='array'的形式赋值,用此种形式会出现类型不匹配错误,可选用strpcopy函数赋值。
例4: 字符型数组s[ ]相当于字符串,但没有存放字符串长度的位置。
{s[1]='n' s[2]='e' s[3]='w' s[4]=0;}
{p-->'new'}
var
s:array[1..10] of char;
p:pchar;
begin
{s:='new'+char(0); error}{错误}
p:=@s[1];
{p:=@s; is not correct}
strpcopy(p,'new');
label1.caption:=strpas(p);{new}
label2.caption:=s;{new}
{label3.caption:=intTostr(integer(s[0]));}{不会出现4, 下标超出错误}
end;
例5:下标从0开始的字符数组s,s相当于@s[0]。
{ s[0]='n' s[1]='e' s[2]='w' s[3]=0;}{p-->'new'}
var
s:array[1..10] of char;
p:pchar;
begin
{s:='new'+char(0); error}{错误}
p:=s;
{p:=@s[0] is also correct}
strpcopy(p,'new');
label1.caption:=strpas(p);{new}
label2.caption:=s;{new}
label3.caption:=s[0];{n}
end;
3、下标从0开始和从1开始的字符数组地址的表示方法也有细微不同:
例6:下标从1开始的数组a 与下标从0开始的数组b 的比较。
var
a:array[1..10]of char;
b:array[0..10]of char;
{a:='1..10';}{type mismatch}
{b:='0..10';}{type mismatch}
begin
strpcopy( b, 'from 0 to 10'); {正确 因为b即是@b[0] }
strpcopy(@b[0], 'from 0 to 10'); {正确 与上个表达式结果相同}
strpcopy(@a[1], 'from 1 to 10'); {正确 }
strpcopy( a, 'from 1 to 10'); {类型匹配错误 因为a即是@a[0]}
end;