Delphi 7 по сравнению с 2009 (& 2010) рекордные размеры - PullRequest
8 голосов
/ 08 июля 2010

У меня странная проблема при преобразовании кода из Delphi 7 в 2010. Это связано с записями. Определенная ниже запись, когда она имеет размер в D7, составляет 432 байта, а в D2009 (и 2010) - 496. Я знаю, что простое решение - сделать ее упакованной записью, тогда все версии получат 426 байтов ... Однако у нас есть данные, хранящиеся в том месте, где мы передавали запись, и теперь мы пытаемся читать эти потоки на более новом языке.

TToTry = Record
 a,b,c,d : Extended;
 e,f,g,h : Extended;
 i : String[15];
 j,k,l,m,n,o,p,q,r,s,t : Array[1..3] of Extended; End;

При изучении этой проблемы я создал еще одну запись, и по какой-то причине размеры совпадают? Запись меньше, но имеет те же типы данных. но он выходит одинакового размера во всех версиях языка.

TMyRecord = Record
Ext1  : Extended;
Ext2  : Extended;
Ext3  : Extended;
Ext4  : Extended;
Ext5  : Extended;
Ext6  : Extended;
Int1  : Integer;
Int2  : Integer;
char1 : AnsiChar;
char2 : AnsiChar;
MyString  : String[15];
Arr1  : Array[1..3] of Extended;
Arr2  : Array[1..3] of Extended; end;

Кто-нибудь может понять, почему одна запись так отличается, а другая такая же? Что-то связанное с выравниванием границ байтов в Delphi наверняка. но что так сильно изменилось от одной версии к другой?

Ответы [ 5 ]

14 голосов
/ 08 июля 2010

Ну, первая проблема в том, что вы сохранили неупакованную запись на диск. Упаковка полей и массивов может изменяться между выпусками продукта, потому что обычно расположение в памяти не видно вне процесса. Вы нарушили это правило.

Если значения по умолчанию для заполнения байтов изменились между Delphi 7 и Delphi 2009, выясните, какие значения по умолчанию были в D7, и установите значения по умолчанию в Delphi 2009.

Также проверьте упаковку массива по умолчанию. Я не могу вспомнить, есть ли отдельная настройка для этого.

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

Если ничего из этого не помогает, создайте временный упакованный тип записи в D2009 и вручную вставьте поля заполнения байтов между фактическими полями данных, пока размер записи и выравнивания полей не будут соответствовать макету D7. Это не просто размер, это выравнивание полей. Прочитайте ваш старый файл данных, используя эту временную упакованную запись. Затем перенесите поле данных по полю в ваш «реальный» тип записи в D2009 и запишите новый файл.

И пока вы занимаетесь этим, упакуйте этот тип записи в D2009, чтобы это больше не повторилось.

7 голосов
/ 09 июля 2010

Я полагаю, что вы включили функцию !. Я не смог получить разумный размер с вашим TToTry с D2007, поэтому мне пришлось искать адреса полей с помощью отладчика;

Во-первых, размер записи ниже,

{$A8}
type
  TToTry = record
    j: array[1..3] of Extended;
    k: array[1..3] of Extended;
    l: array[1..3] of Extended;
    m: array[1..3] of Extended;
  end;

равно 128 (32 * 4). Это ожидается, поскольку Extended равен 10 байтов, 30 байтов будут выровнены по 32 байта.

Но размер этой записи,

{$A8}
type
  TToTry = record
    j, k, l, m: array[1..3] of Extended;
  end;

- это 120 (30 * 4). Это, безусловно, неожиданно - поля все равно должны выравниваться на 8-байтовой границе.

(у меня нет D7 для проверки, но я так думаю :)
Итак, теперь мы знаем, что сгруппированные поля упакованы, из этого следует, что выравнивание на D7 составляет 8 байт, а ваша запись почти упакована;

TToTry = Record 
 a,b,c,d : Extended;    // 40 bytes (8*5)
 e,f,g,h : Extended;    // 40 bytes (8*5)
 i : String[15];        // 16 bytes (8*2)
 j,k,l,m,n,o,p,q,r,s,t: Array[1..3] of Extended; // 330 bytes
End; 

Компилятор добавляет 6 байтов к последней группе, чтобы она была кратна 8, а затем вы получите 40 + 40 + 16 + 336 = 432 байта.

С D2009 / D2010 вы либо объявляете каждое поле - без группировки, либо поведение изменяется. В любом случае упакуйте вашу запись и добавьте в конец фиктивное поле массива из 6 байтов, и все будет хорошо.

Если это не сработает, посмотрите на адреса полей вашей записи с помощью D7, затем создайте точный дубликат на D2009, используя упакованную запись и, при необходимости, используя фиктивные поля, после того как вы импортировали сохраненные данные, которые вы можете удалить фиктивные поля.

-
Я никогда не знал такого поведения, и я нигде не могу найти его документированным. Тем не менее, это так похоже на функцию, что я не решаюсь назвать это ошибкой. Я не знаю, что такое же поведение с D2009 или D2010, проверьте, если это так. Если это так, чтобы получить ожидаемые результаты - не иметь наполовину упакованных записей - не ленитесь и объявляйте каждое поле отдельно для неупакованных записей.

3 голосов
/ 22 октября 2013

Я знаю, что это старый пост, но вчера я столкнулся с той же проблемой с Delphi XE2. У меня есть несколько типизированных файлов, которые были записаны с использованием приложения Turbo Delphi 2006, и я также допустил ошибку, не используя упакованные записи. Моя ситуация была сложной, потому что я использовал записи переменной длины (это то, что мы называем их в мире z / OS, а не на 100% уверен в термине Delphi), поэтому неправильное выравнивание приводило к тому, что теги ключа записи не заканчивались в правильное поле, в результате чего ни одна из вложенных записей не окажется в нужном месте, что делает весь файл бесполезным.

Я обнаружил, что вы можете разместить директивы компилятора выравнивания вокруг определенного блока кода. Наряду с этим я также обнаружил этот маленький драгоценный камень: {$OLDTYPELAYOUT ON}

{$OLDTYPELAYOUT ON}
 RMasterRecord = Record       
    mKey       : word;
    mDeleted   : boolean;
    case mType : ANSIChar of
       'V' : //Info
          (Vers : RVersionLayout);
       […]
{$OLDTYPELAYOUT OFF}

Я помещаю директиву только вокруг основной записи, которая записывается и читается в файл, а не вокруг определений подзаписей. Это решило мою проблему, и теперь я могу скомпилировать в XE2 и прочитать мои файлы TD2006. В следующий раз я буду использовать упакованные записи (или еще лучше, SQLite). Но я думал, что поделюсь этим, так как этот сайт помог мне неизмеримо в течение многих лет.

Вы можете узнать больше о $ OLDTYPELAYOUT здесь: http://docwiki.embarcadero.com/RADStudio/XE5/en/Internal_Data_Formats#Record_Types. Кстати, первый, который исправил это, был {$A4}, но когда я обнаружил {$OLDTYPELAYOUT ON}, я переключил его, чтобы использовать это, так как это больше очевидно.

2 голосов
/ 09 июля 2010

Применение директивы {$ A8} не означает, что все поля записи выровнены по 8-байтовой границе - компилятор использует другую стратегию выравнивания.Например, размер

{$A8}
type
  TToTry = record
    a: byte;
    b: word;
    c: longword;
  end;

в Delphi 2009 составляет 8 байт, поскольку компилятор выравнивает 2-байтовое значение на 2-байтовой границе, 4-байтовое значение на 4-байтовой границе и является единственным действительнымвыравнивание в приведенном выше примере - это выравнивание поля b по 2-байтовой границе.

Что касается исходного вопроса, что изменилось между Delphi 7 и Delphi 2009 - прочитайте ответ Sertac Akyuz и мой комментарий к нему

2 голосов
/ 08 июля 2010

Я считаю, что выравнивание по умолчанию было сделано шире.Укажите выравнивание 4 в более поздних версиях и посмотрите, получится ли оно так, как вы хотите.

В будущем вы должны убедиться, что любая запись, которая будет записана на диск, будет сохранена упакованной, чтобы вы неЭто не сгорит.

Редактировать: Поскольку ни одно из выравниваний не работает (что меня удивляет), я бы вернулся к оригиналу и выяснил, как оно действительно выровнено.Заполните запись чем-то вроде $ FF, вставьте данные и запишите их - посмотрите, где выжили $ FF.Возьмите новую запись, соберите ее и добавьте наполнители, чтобы они соответствовали отступам в старой записи.

Одна вещь: на самом деле это всего лишь одна запись?В старые времена я использовал объекты в качестве поддельных записей с наследованием - упс, в момент наследования было применено нормальное выравнивание, и я не мог его остановить.В итоге мне пришлось дописать данные перед тем, чтобы принудительное выравнивание не нарушило мои данные.(Это было API, это HAD , чтобы быть правым, я не мог обработать поля независимо.)

...