Есть ли разница между массивом и упакованным массивом в Delphi?

В C/С++ у вас всегда есть

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

В Pascal/Delphi вы можете использовать "упакованный массив", чтобы быть уверенным, что указанное выше утверждение верно, но имеет ли спецификатор "упакованный" какое-либо практическое значение для массивов в 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/С++ и записи Delphi различны - их можно распаковать так, чтобы размер структуры был больше, чем сумма размеров полей из-за выравнивания полей.)

Ответы

Ответ 1

Он не имеет практического эффекта в Delphi. Единственный тип, на который он мог бы разумно повлиять, - это тип с самой сложной комбинацией выравнивания и размера, Extended, размер которой равен 10 и выравнивание 8. Однако массивы Extended по существу уже упакованы (хотя они все еще имеют выравнивание 8, если директива packed работала так же, как и в записях, они имели бы выравнивание 1).

Почему я говорю, что массивы Extended - единственный тип, на который это может повлиять? Нет другого типа Delphi, встроенного или который вы можете создать, который имеет размер, который не является целым числом, кратным его выравниванию (оставляя в стороне старые версии Delphi и некоторые ошибки). Выравнивание - это то, что делает записи большими с заполнением; он вызывает разнесение полей, так что каждое поле начинается со смещения, которое является целым числом, кратным его выравниванию по типу. В аналогичном случае с массивами задействован только один тип, и если размер уже кратен выравниванию типа, тогда нет необходимости в дополнении.

Здесь программа, которая показывает, как Extended влияет на размер и выравнивание в зависимости от того, завершена ли она в записи или нет; вы можете добавить packed в массивы и посмотреть, что это не имеет значения:

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.

Больше слов о выравнивании и packed: packed имеет другой эффект (по записям) вместо того, чтобы просто гарантировать, что добавление дополнений не добавлено: оно также отмечает запись как имеющую выравнивание 1. Это имеет отрицательный эффект, вызвавший его частое смещение, когда он используется в другом месте. Для обеспечения совместимости языка/ОС только в том случае, если другой язык не использует правила выравнивания ОС (обычно это означает правила выравнивания по С), следует использовать упакованную директиву. (Некоторые заголовки API Windows имеют неправильное выравнивание для типов, определенных внутри них, помните о вас, и им приходилось жить с ним с тех пор.) С другой стороны, с точки зрения совместимости с форматом файлов, они могут быть оправданы, но существуют существует множество других проблем в отношении выбора типа (например, Integer был 2 байта в 16-битном Delphi, но 4 байта впоследствии).

Delphi пытается использовать C-совместимые правила для выравнивания. В прошлом у него были некоторые ошибки (особенно с такими записями, как TRec = запись A, B: Extended end, по сравнению с TRec = запись A: Extended; B: Extended end;), но теперь эти ошибки должны быть исправлены

Ответ 2

Справка Delphi XE говорит об этом для динамических массивов

Макет динамической памяти массива (только для Win32):

Содержание смещения

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

Итак, этим документом он упакован.