最近,在使用Delphi数组时,我遇到了一些问题,这些问题使我对其进行了更仔细的考虑。
我写了一个测试函数:
procedure TForm1.Button2Click(Sender: TObject);
var
MyArray1: array[0..0] of UInt64;
MyArray2: array of UInt64;
Value1, Value2: UInt64;
begin
SetLength(MyArray2, 1);
MyArray1[0] := 100;
MyArray2[0] := 100;
Value1 := PUInt64(@MyArray1)^;
Value2 := PUInt64(@MyArray2)^;
Value1 := PUInt64(@MyArray1[0])^;
Value2 := PUInt64(@MyArray2[0])^;
//Value1 := PUInt64(MyArray1)^;
Value2 := PUInt64(MyArray2)^;
end;
Run Code Online (Sandbox Code Playgroud)
以我的理解,静态数组存储第一个元素,第二个元素等的值。而动态数组存储数组的地址。
因此,PUInt64(@ MyArray2)^实际上将包含64位计算机中阵列的地址,而其中一半包含32位计算机中的地址。
但是,为什么PUInt64(MyArray1)^是无效的强制转换?
而且似乎PUInt64(@ MyArray2 [0])^是最安全的转换,因为它可同时用于静态和动态数组。那是对的吗?
首先,让我首先赞扬您编写了一个如此精心研究的问题!
基本上,您似乎大部分都正确。在Delphi中,静态数组是一种值类型,例如单个整数或整数记录。使用sizeof任何这样的变量,你得到的数据的完整大小。另一方面,动态数组是引用类型。该变量仅存储指向实际数组数据的单个指针。因此sizeof,在动态数组上仅产生指针的大小。
当然,这也会影响分配的结果:如果使用复制静态数组a := b,则会复制所有数据,最后得到两个独立的数组。如果复制动态数组,则仅复制指针,最后以指向同一数据的两个指针结束。
因此,回到您的代码:
Value1 := PUInt64(@MyArray1)^;
Run Code Online (Sandbox Code Playgroud)
是的,因为MyArray1“是”的数据,与开始100,@MyArray1是一个指针100的值。类型转换是正确的(因为数组中的数据类型为UInt64,指向该值的指针的类型为PUInt64),并且通过解引用,您得到100。
Value2 := PUInt64(@MyArray2)^;
Run Code Online (Sandbox Code Playgroud)
是的,因为MyArray2是指向实际数据的指针,所以是指向实际数据@MyArray2的指针的指针。在64位进程中,指针是64位整数,因此类型转换有效。通过取消引用,可以将原始指针恢复为实际数据。但是在32位进程中执行此操作是一个错误。
更简单地说,您可以写(*)
Value2 := NativeUInt(MyArray2);
Run Code Online (Sandbox Code Playgroud)
让我们继续
Value1 := PUInt64(@MyArray1[0])^;
Run Code Online (Sandbox Code Playgroud)
这很容易:MyArray1[0]是your 100,然后您获取地址,然后取消引用指针以返回原始值。
Value2 := PUInt64(@MyArray2[0])^;
Run Code Online (Sandbox Code Playgroud)
此处完全相同。
和
Value2 := PUInt64(MyArray2)^;
Run Code Online (Sandbox Code Playgroud)
基本上是我上面提到的情况(在*处),只是取消引用了。在32位和64位应用程序中均有效。(PUInt64由于的原因,P它具有本机大小;它可能是32位。)实际上,MyArray2 它是指向an的指针UInt64,即a PUInt64,因此通过取消引用它,就可以得到该UInt64值(100)。
然而,
Value1 := PUInt64(MyArray1)^;
Run Code Online (Sandbox Code Playgroud)
是一个错误。MyArray1就您而言,与完全相同UInt64。这不是指向这样的事情的指针-它不是一个PUInt64。当然,您可以向编译器撒谎,并告诉它:“嘿,将此视为指向的指针UInt64”。但是通过取消引用它,您尝试获取UInt64at地址100,这将导致访问冲突,因为您不拥有内存中的该区域(很可能)。
由于大小匹配,因此在64位处理中,代码将进行编译。PUInt64具有指针的大小(64位),并且MyArray1在声明中具有64位的大小。
在32位进程中,由于大小不匹配,因此代码将无法编译。PUInt64具有指针的大小(32位),但是MyArray1在声明中具有64位的大小。
而且似乎PUInt64(@ MyArray2 [0])^是最安全的转换,因为它可同时用于静态和动态数组。那是对的吗?
通常,我觉得静态数组和动态数组是两个非常不同的东西,因此我不会尝试在两种情况下都强制代码看起来相同。
您是否要获取数组中的第一个值?如果是这样,只需写MyArray2[0]。(或者MyArray1[0]在静态数组的情况下。)您是否在尝试获取第一个元素的地址?如果是这样,我更喜欢NativeUInt(MyArray2)代替NativeUInt(@MyArray2[0])(或pointer或PUInt64)。实际上,如果数组为空,则第一种方法会产生0或nil,而另一种则是错误。
更新:
为了弄清楚,如果a是静态数组,则第一个元素的地址就是@a(或@a[0])。如果b为动态数组,则第一个元素的地址为pointer(b)(或@b[0])。您可以将任何指针转换为其他一些指针大小的类型,例如NativeUInt(整数类型)或PUInt64(特定指针类型)或PCardinal(特定指针类型)或...。那不会改变实际值,只会改变其编译时的解释。
| 归档时间: |
|
| 查看次数: |
124 次 |
| 最近记录: |