首先, 只有字符类型的数组才可以直接转换为字符串!
//这是静态数组的例子: var Arr: array[0..9] of Char; s: string; p: PChar; i: Integer; begin {给 Arr 赋值: A B C D E F G H I J} for i := Low(Arr) to High(Arr) do Arr[i] := Chr(65+i); {把 Char 数组赋给 string} s := Arr; ShowMessage(s); {ABCDEFGHIJ} {把 Char 数组赋给 PChar} p := PChar(string(Arr)); ShowMessage(p); {ABCDEFGHIJ} {直接把 Char 数组赋给 PChar, 会有意想不到的结果, 因为缺少 #0 结束} p := Arr; //p := @Arr; {或者这样} //p := @Arr[0]; {再或者这样} ShowMessage(p); {ABCDEFGHIJ未知数据} {这种方式偏偏是我们在使用 API 函数时用得最多的, 但 API 函数都会返回给 #0 结束的, 所以没有问题} end; //这是动态数组的例子: var Arr: array of Char; s: string; p: PChar; i: Integer; begin {给 Arr 赋值: A B C D E F G H I J} for i := 0 to 9 do begin SetLength(Arr, i+1); Arr[i] := Chr(65+i); end; Pointer(s) := Arr; //Pointer(s) := @Arr[0]; {或者这样} ShowMessage(s); {ABCDEFGHIJ} p := PChar(Arr); //p := @Arr[0]; {或者这样} ShowMessage(p); {ABCDEFGHIJ} end;
要想彻底理解上面的操作, 需要知道静态数组与动态数组指针问题.
var ArrS: array[0..9] of Char; {静态数组} ArrD: array of Char; {动态数组} i: Integer; begin {设置动态数组大小, 并给两个数组赋值} SetLength(ArrD, 10); for i := 0 to 9 do begin ArrS[i] := Char(65+i); ArrD[i] := Char(97+i); end; {测试赋值结果} ShowMessageFmt('%s, %s', [ArrS[0], ArrD[0]]); {A, a} {静态数组变量的指针(而非变量本身)和它的第一个元素的指针是同一个} ShowMessageFmt('%p, %p', [@ArrS[0], @ArrS]); {0012FDE2, 0012FDE2} {动态数组变量本身(而非变量的指针)就是一个指针, 它和第一个元素的指针也是同一个} ShowMessageFmt('%p, %p, %p', [ArrD, @ArrD[0], @ArrD]); {00E7C970, 00E7C970, 0012FDEC} end;
另外, 动态数组的构造和静态数组完全不同, 它和 String 的构造 倒是有些相似.
每个动态数组第一个元素前还有 8 个字节, 没 4 个字节记录一个整数;
最前面 4 个字节是用于生存管理的引用计数(当引用计数为 0 时数组自动释放);
第一个元素之前的 4 个字节记录数组的长度. 测试如下:
var Arr1,Arr2: array of Char; {定义两个动态数组, 其中一个是为了测试引用计数} i: Integer; {用于指针运算} p: PInteger; {用于读取动态数组前面的两个 32 位整数} begin SetLength(Arr1, 10); {设置数组长度} Arr2 := Arr1; {增加一个引用} i := Integer(Arr1); {获取数组地址(这也是数组第一个元素的位置)} {获取动态数组的长度} i := i-4; p := PInteger(i); ShowMessage(IntToStr(p^)); {10} {获取动态数组的引用计数} i := i-4; {再减 4} p := PInteger(i); ShowMessage(IntToStr(p^)); {2} end;