我有一个双倍xyInput := VarArrayCreate([0, Count-1, 0, 1], varDouble );
的2D OleVariant矩阵。
我希望使用DestArray : array[0..1] of array of Double
转换(尽可能快)到普通的2D动态数组move()
。
在解决此问题的过程中,我使用Count=5
为每个维度提供了预期的40个字节。但我发现Pointer(DestArray[0])
和Pointer(DestArray[1])
之间的地址差异为56个字节。
那么中间的16个字节是什么?我不知道前8个字节,但最后8个字节是关于数组维度的信息。
因此,move()
无法一步完成Move(VarArrayData^, Pointer(DestArray[0])^, BytesToMove );
。
我找到了一种方法,通过使用2个单独的动作,但我仍然觉得它可以做得更优雅。
问题:
move()
在一个步骤中工作?Pointer(DestArray[0])
和Pointer(DestArray[1])
之间的内存空白中的前8个字节是什么?以下是完整的代码段:
procedure TForm1.FormCreate(Sender: TObject);
procedure PrintEqualityVerdictLine( value1 : Double; value2 : Double );
const
cEqualVerdict : array[Boolean] of String = ( '!!!Not Equal!!!', 'Equal' );
begin
Memo1.Lines.Add(FloatToStr(value1) + ' =? ' + FloatToStr(value2) + ' ' + cEqualVerdict[ SameValue( value1, value2, 0.001 ) ] );
end;
procedure VariantArrayOfDoubleToDynamicDoubleArray;
var
xyInput : OleVariant;
Count: Integer;
n: Integer;
DestArray : packed array[0..1] of packed array of Double;
V_Ptr: PVarData;
VarArrayData: PVarData;
BytesToMove: Integer;
SourceBytePtr : PByte;
BytesToMovePerColumn: Integer;
DestBytePtr: PByte;
begin
// create 2 column OleVariant array:
Count := 5;
xyInput := VarArrayCreate([0, Count-1, 0, 1], varDouble );
// fill test data:
for n := 0 to Count-1 do
begin
xyInput[n, 0] := 1.0 * n;
xyInput[n, 1] := 2.0 * Count + n;
end;
SetLength(DestArray[0], Count);
SetLength(DestArray[1], Count);
V_Ptr := PVarData(@xyInput);
if ((V_Ptr^.VType and $F000 ) = varArray) and
((V_Ptr^.VType and varTypeMask ) = varDouble)
then
begin
VarArrayData := PVarData(V_Ptr^.VArray^.Data);
BytesToMovePerColumn := Count * V_Ptr^.VArray^.ElementSize;
BytesToMove := BytesToMovePerColumn*V_Ptr^.VArray^.DimCount;
// print 16 discovered intermediate bytes of the DestArray:
DestBytePtr := Pointer(DestArray[0]);
Inc(DestBytePtr, BytesToMovePerColumn);
for n := 1 to 16 do
begin
Memo1.Lines.Add('byte['+IntToStr(n) + ']: ' + IntToStr( DestBytePtr^ ) );
Inc(DestBytePtr);
end;
// This does NOT work: col 1 of arr gets offset due to 16 discovered intermediate bytes:
// Move(VarArrayData^, Pointer(DestArray[0])^, BytesToMove );
// This works:
SourceBytePtr := PByte(VarArrayData);
Move(SourceBytePtr^, Pointer(DestArray[0])^, BytesToMovePerColumn );
Inc(SourceBytePtr, BytesToMovePerColumn);
Move(SourceBytePtr^, Pointer(DestArray[1])^, BytesToMovePerColumn );
end;
// print:
Memo1.Lines.Add('VariantArrayOfDoubleToDoubleArray:');
Memo1.Lines.Add('col 0:');
for n := 0 to Count - 1 do
PrintEqualityVerdictLine( xyInput[n, 0], DestArray[0, n] );
Memo1.Lines.Add('');
Memo1.Lines.Add('col 1:');
for n := 0 to Count - 1 do
PrintEqualityVerdictLine( xyInput[n, 1], DestArray[1, n] );
end;
begin
Memo1.Lines.Clear;
VariantArrayOfDoubleToDynamicDoubleArray;
end;
答案 0 :(得分:4)
但我发现
Pointer(DestArray[0])
和Pointer(DestArray[1])
之间的地址差异为56个字节。
这是非常值得期待的。好吧,为了更清楚,没有理由期望DestArray[0]
和DestArray[1]
指向相邻的内存块。
您的类型是
array[0..1] of array of Double;
请注意,我删除了应用于数组时忽略的packed
关键字。你在这里有一个包含两个指针的数组。这两个指针是独立的。看看如何分配动态数组。
SetLength(DestArray[0], Count);
SetLength(DestArray[1], Count);
每次调用SetLength
都会导致单独的堆分配。毫无理由让内存相邻。在此之前,动态数组在数组的有效负载之前存储了额外的元数据块,并且每个内存块都有自己的内存管理器使用的元数据。因此,即使内存管理器偶然发生了相邻的内存块,元数据也会位于两个数组之间。顺便提一下,这个内存管理器元数据是你问题3的答案。
在技术方面,DestArray
中的内容为jagged array。另一方面,您似乎正在寻找一个多维数组。 Delphi实际上并不支持动态多维数组。你所拥有的只是锯齿状阵列。如果你想要一个连续的内存块,那么你需要分配一个内存块并自己执行索引计算。
因此,就目前而言,如果继续使用锯齿状数组,则需要为每个内部数组执行一个副本。如果切换到线性阵列,那么您可以使用单个副本,但您必须执行自己的索引。当然,索引很容易做到,而且效率可能很高。最后,您可以提前在Delphi中分配线性数组,并将指向该数组的指针放入您的变体中,从而完全避免复制。