Выравнивание полей в записи (Record)

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Выравнивание полей в записи (Record)

Сообщение Mikha » 21.07.2010 21:50:40

Код из справки (ref) по fpc
Код: Выделить всё
Program DemoRecords;

type
  {$PackRecords 2}
  TRec1 = Record
    A : Byte;
    B : Word;
  end;
  {$PackRecords 1}
  TRec2 = Record
    A : Byte;
    B : Word;
  end;
  {$PackRecords 2}
  TRec3 = Record
    A,B : Byte;
  end;
  {$PackRecords 1}
  TRec4 = Record
    A,B : byte;
  end;
  {$PackRecords 4}
  TRec5 = Record
    A : Byte;
    B : Array[1..3] of byte;
    C : Byte;
  end;
  {$PackRecords 8}
  TRec6 = Record
    A : Byte;
    B : array[1..3] of byte;
    C : byte;
  end;
  {$PackRecords 4}
  TRec7 = Record
    A : Byte;
    B : Array[1..7] of byte;
    C : Byte;
  end;
  {$PackRecords 8}
  TRec8 = Record
    A : Byte;
    B : Array[1..7] of byte;
    C : byte;
  end;
  Var
    rec1 : TRec1;
    rec2 : TRec2;
    rec3 : TRec3;
    rec4 : TRec4;
    rec5 : TRec5;
    rec6 : TRec6;
    rec7 : TRec7;
    rec8 : TRec8;
  begin
    Write('Size TRec1 : ',SizeOf(TRec1));
    WriteLn(' Offset B : ',Longint(@Rec1.B)-Longint(@Rec1));
    Write('Size TRec2 : ', SizeOf(TRec2));
    WriteLn(' Offset B : ',Longint(@Rec2.B) - LongInt(@Rec2));
    Write('Size TRec3 : ',SizeOf(TRec3));
    WriteLn(' Offset B : ',LongInt(@Rec3.B) - Longint(@Rec3));
    Write('Size TRec4 : ',SizeOf(TRec4));
    WriteLn(' Offset B : ',Longint(@Rec4.B) - Longint(@Rec4));
    Write('Size TRec5 : ', SizeOf(TRec5));
    WriteLn(' Offset B : ', Longint(@Rec5.B) - Longint(@Rec5),
            ' Offset C : ', Longint(@Rec5.C) - LongInt(@Rec5));
    Write('Size TRec6 : ', SizeOf(TRec6));
    WriteLn(' Offset B : ', Longint(@Rec6.B) - Longint(@Rec6),
            ' Offset C : ', Longint(@Rec6.C) - Longint(@Rec6));
    Write('Size TRec7 : ', SizeOf(TRec7));
    WriteLn(' Offset B : ', Longint(@Rec7.B) - Longint(@Rec7),
            ' Offset C : ', Longint(@Rec7.C) - Longint(@Rec7));
    Write('Size TRec8 : ', SizeOf(TRec8));
    WriteLn(' Offset B : ', Longint(@Rec8.B) - Longint(@Rec8),
            ' Offset C : ', Longint(@Rec8.C) - Longint(@Rec8));
    ReadLn;
end.

В соответствии со справкой должен получить следующий результат:
Size Trec1 : 4 Offset B : 2
Size Trec2 : 3 Offset B : 1
Size Trec3 : 2 Offset B : 1
Size Trec4 : 2 Offset B : 1
Size Trec5 : 8 Offset B : 4 Offset C : 7
Size Trec6 : 8 Offset B : 4 Offset C : 7
Size Trec7 : 12 Offset B : 4 Offset C : 11
Size Trec8 : 16 Offset B : 8 Offset C : 15
Фактически получаю следующее:
Size TRec1 : 4 Offset B : 2
Size TRec2 : 3 Offset B : 1
Size TRec3 : 2 Offset B : 1
Size TRec4 : 2 Offset B : 1
Size TRec5 : 5 Offset B : 1 Offset C : 4
Size TRec6 : 5 Offset B : 1 Offset C : 4
Size TRec7 : 9 Offset B : 1 Offset C : 8
Size TRec8 : 9 Offset B : 1 Offset C : 8
1.Почему не сходятся результаты справки и фактические
2.Почему размер 7 и 8 записи по справке равен 12 и 16, считаю что оба должны быть равны 12 (2+8+2=12)
P.S. fpc 2.4.0, Windows XP
Mikha
незнакомец
 
Сообщения: 3
Зарегистрирован: 21.07.2010 21:09:48

Re: Выравнивание полей в записи (Record)

Сообщение Brainenjii » 22.07.2010 04:37:33

Попробуйте сделать
Код: Выделить всё
  TRec1 = Packed Record
    A : Byte;
    B : Word;
  end;
Аватара пользователя
Brainenjii
энтузиаст
 
Сообщения: 1351
Зарегистрирован: 10.05.2007 00:04:46

Re: Выравнивание полей в записи (Record)

Сообщение Mikha » 26.07.2010 20:48:28

>>Brainenjii
Попробовал, результат ожидаемый - все записи имеют минимальный размер (выровнены по границе одного байта);
Как работает выравнивание (если оно вообще работает) так и не понял. Даже простейший пример дает не те результаты, которые я ожидаю:
Код: Выделить всё
procedure PackRecordsDemo2;
type
  {$PackRecords 1}
  TRec7 = Record
    A : Byte;
    B : Array[1..7] of byte;
    C : Byte;
  end;
  {$PackRecords 2}
  TRec8 = Record
    A : Byte;
    B : Array[1..7] of byte;
    C : Byte;
  end;
var
  Rec7 : TRec7;
  Rec8 : TRec8;
begin
  WriteLn('Size TRec7 : ',SizeOf(Rec7));
  WriteLn('Size TRec8 : ',SizeOf(Rec8));
  ReadLn;
end;

Вывод программы:
9
9
Должно быть:
9(1+7+1)
12(2+8+2)
:?:
Mikha
незнакомец
 
Сообщения: 3
Зарегистрирован: 21.07.2010 21:09:48

Re: Выравнивание полей в записи (Record)

Сообщение zub » 27.07.2010 14:32:29

тоже сталкивался с "неправильным" выравниванием в случае наличия внутри object`а массива. в разных версиях fpc были разные результаты, пришлось использовать выравнивание по границе байта
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Выравнивание полей в записи (Record)

Сообщение Mikha » 28.07.2010 19:25:56

Нашел ответ на свой вопрос: http://wiki.freepascal.org/User_Changes_2.4.0#Alignment_of_record_variables
Если вкратце, то чтобы всегда быть уверенным в размере записи ее необходимо объявить как упакованную (packed). Про опцию {$PackRecords N} можно забыть - в общем случае она не действует.
Mikha
незнакомец
 
Сообщения: 3
Зарегистрирован: 21.07.2010 21:09:48


Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

Рейтинг@Mail.ru