15

在 C/C++ 中,你总是有

SizeOf(array[N] of T) = N * SizeOf(T);

在 Pascal/Delphi 中,您可以使用 'packed array' 来确保上述断言是正确的,但是 'packed' 说明符对 Delphi 中的数组有任何实际价值吗?我无法创建“未打包”数组的示例,这些数组似乎总是“打包”:

type
  A = array[0..2] of Byte;
  B = array[0..99] of A;
  C = packed record
    C1, C2, C3: Byte;
  end;
  D = array[0..99] of C;

procedure TForm10.Button1Click(Sender: TObject);
begin
  Assert(SizeOf(A) = 3);
  Assert(SizeOf(B) = 300);
  Assert(SizeOf(D) = 300);
end;

(C/C++ 结构和 Delphi 记录是不同的——它们可以被“解包”,因此由于字段对齐,结构的大小大于字段大小的总和。)

4

2 回答 2

27

It has no practical effect in Delphi. The only type it could reasonably affect is the type with the oddest alignment and size combination, Extended, which has a size of 10 and an alignment of 8. However, arrays of Extended are essentially packed already (though they still have an alignment of 8; if the packed directive worked like it did on records, they would have an alignment of 1).

Why do I say arrays of Extended is the only type it could affect? There is no other Delphi type, built-in or that you can compose, which has a size that is not an integer multiple of its alignment (leaving aside older versions of Delphi, and some bugs). Alignment is the thing that makes records larger with padding; it causes fields to be spaced out so that every field starts at an offset which is an integer multiple of its type's alignment. In the analogous case with arrays, there is only one type involved, and if the size is already a multiple of the type's alignment, then there's no need for padding.

Here's a program which shows how Extended affects size and alignment depending on whether it's wrapped in a record or not; you can add packed to the arrays, and see it makes no difference:

type
  TWrap = record
    X: Extended;
  end; // field size=10, align=8, => actual size=16

  TArr1 = array[1..3] of TWrap; // 3*16 => size=48, align=8
  TArr2 = array[1..3] of Extended; // 3 * 10 => size=30, align=8

  TRec1 = record
    A: Byte;
    B: TArr1;
  end;

  TRec2 = record
    A: Byte;
    B: TArr2;
  end;

var
  x: TRec1;
  y: TRec2;
begin
  Writeln('Size of TArr1: ', SizeOf(TArr1));
  Writeln('Alignment of TArr1: ', Integer(@x.B) - Integer(@x.A));
  Writeln('Size of TArr2: ', SizeOf(TArr2));
  Writeln('Alignment of TArr2: ', Integer(@y.B) - Integer(@y.A));
end.

More words about alignment and packed: packed has another effect (on records) rather than just guaranteeing that there is no padding added: it also marks the record as having itself an alignment of 1. This has the negative effect of causing it to be frequently misaligned when it is used elsewhere. For purposes of language / OS interoperability, only in the case where the other language is not using OS alignment rules (normally meaning C alignment rules) should the packed directive be used. (Some Windows API headers have incorrect alignment for types defined within them, mind you, and have had to live with it ever since.) For purposes of compatibility with a file format, on the other hand, packed may be justified, but there are lots of other concerns there too, with respect to type choice (e.g. Integer was 2 bytes in 16-bit Delphi, but 4 bytes subsequently).

Delphi tries to use C-compatible rules for alignment. In the past, it had some bugs here (particularly with records like TRec = record A, B: Extended end; versus TRec = record A: Extended; B: Extended end;), but these bugs should be fixed now

于 2011-01-03T12:11:24.857 回答
2

Delphi XE Help 对动态数组说这个

动态数组内存布局(仅限 Win32):

偏移内容

-8  32-bit = reference-count  
-4  32-bit = length indicator (number of elements)  
0..Length * (size of element) -1 = array elements 

因此,该文档已打包。

于 2013-06-17T18:13:10.577 回答