Delphi 7 по сравнению с 2009 г. (и 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. но что так кардинально изменилось от одной версии к другой?


person Jason Nethercott    schedule 08.07.2010    source источник
comment
Я знаю, что выравнивание байтов по умолчанию для записей Delphi изменилось в последней версии (думаю, 2009 г.), но я не уверен в деталях.   -  person Mason Wheeler    schedule 08.07.2010


Ответы (5)


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

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

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

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

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

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

person dthorpe    schedule 08.07.2010
comment
Все это предполагает, что у вас нет возможности вернуться к D7 и изменить код там, чтобы упаковать структуру записи и выписать файл упакованных записей. Это будет меньше работы, чем пытаться вручную сопоставить заполнение байтов постфактум. - person dthorpe; 08.07.2010
comment
Заднее зрение всегда 20/20 ... и прогрессивное развитие (и разработчиков) не всегда происходит. Хорошая идея насчет забитой пластинки с пробелами за 2009/2010 гг. Мы попробуем. - person Jason Nethercott; 08.07.2010
comment
Взгляд в прошлое тоже должен быть поучительным. Запись распакованных записей в файл должна происходить только один раз за карьеру. ; › - person dthorpe; 08.07.2010
comment
Это не совсем ответ на вопрос. Был задан вопрос, почему правила компоновки записей, которые изменились между двумя версиями Delphi, повлияли на первую запись, но не на вторую. Этот ответ дает предложения о том, как решить проблему, возникшую в результате этих изменений, но вопрос не об этом. Что такого в TToTry, что делает его восприимчивым к различным правилам выравнивания, тогда как TMyRecord кажется невосприимчивым? - person Rob Kennedy; 09.07.2010
comment
Я не могу ответить на вопросы политики о том, почему команда разработчиков продукта решила внести то или иное изменение. Что касается того, почему размер TMyRecord остается стабильным между выпусками, проверял ли кто-нибудь файл проекта, чтобы узнать, установлено ли выравнивание явно? TMyRecord также сильно отличается от TToTry. В TToTry преобладают 8 10-байтовых расширенных полей и 12 массивов из 3 10-байтовых расширенных полей. TMyRecord добавляет целые числа и байты и намного меньше расширяет 10 байтов. В TMyRecord гораздо меньше элементов, которые могут нуждаться в заполнении для выравнивания N слов. - person dthorpe; 09.07.2010
comment
@Rob: Забыл, что вы относительно новичок в Delphi. Разрешите представить Дэнни Торпа. Дэнни не был главным инженером компилятора Delphi в течение нескольких лет и поэтому не знал, почему были внесены недавние изменения. Однако он достаточно квалифицирован, чтобы объяснить, как решить проблему (и проанализировать, почему проблема существует). :-) - person Ken White; 09.07.2010
comment
Это относительно новое замечание было шуткой, верно? Я знаю, кто он, @ Кен. В опубликованном вопросе не спрашивалось, почему было внесено изменение, как и мой последующий комментарий. Вопрос действительно спрашивал, почему изменение повлияет на одну запись, а не на другую, и я думаю, что ответ Дэнни (в комментарии) на этот вопрос был довольно расплывчатым. И хотя это практическая информация, вопрос не касался того, как исправить эти различия. - person Rob Kennedy; 09.07.2010
comment
@ Роб: Вы не видели смайлик? Я могу указать на это - это как раз перед «- Кен Уайт» в конце моего комментария. Но мой комментарий все еще в силе - если вы не являетесь членом команды компиляторов, которая работала над версией компилятора для Delphi 2009, вы не можете ответить на вопрос, почему она изменилась; поэтому ответ Дэнни был настолько точным предположением, насколько это мог сделать кто-либо еще (и лучше, чем ответ, который вы не предоставили ‹g›), и его первое предложение было точным. (Вы ведь видите здесь ‹g› в моем предыдущем предложении, верно? Это прямо перед закрывающей круглой скобкой.) - person Ken White; 09.07.2010
comment
@Ken, я просто не был уверен, применим ли смайлик в конце вашего комментария к шутке в начале, потому что между ними не было шуток. В любом случае, я знаю, что мы не можем ответить, почему это изменилось, но что с того? Об этом никто никогда не спрашивал. Первое предложение комментария Дэнни мертво при ответе на несуществующий вопрос. Я подумал о том, чтобы опубликовать ответ, описывающий, как определить, где добавить байты заполнения к записи, но я проверил себя; Я понял, что не отвечаю на то, что спросил Джейсон. Фактически, Сертак - тот, кто погиб. - person Rob Kennedy; 10.07.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, проверьте, так ли это. Если это так, чтобы получить ожидаемые результаты - не иметь наполовину упакованных записей - не поленитесь и объявляйте каждое поле отдельно для незапакованных записей.

person Sertac Akyuz    schedule 09.07.2010
comment
Размер второй записи в Delphi 2009 - 128 байт. - person kludg; 09.07.2010
comment
@Serg - спасибо, что искали, так что это не функция. Фактически, я столкнулся с другими странными / неожиданными выравниваниями во время тестирования с D2007. Я рад, что в более поздних версиях все обстоит правильно! - person Sertac Akyuz; 09.07.2010

Я знаю, что это старый пост, но вчера я столкнулся с той же проблемой с 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}, я переключил его на использование этого, поскольку это более очевидно.

person Jeff    schedule 22.10.2013

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

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

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

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

person Loren Pechtel    schedule 08.07.2010
comment
Как указать выравнивание? - person Jason Nethercott; 08.07.2010
comment
Нашел: {$ A4}. Но это не помогло. Теперь число 464. Я попробовал все другие варианты {$ A?}, Но ни один не подошел. - person Jason Nethercott; 08.07.2010

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

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

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

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

person kludg    schedule 09.07.2010
comment
Ах хорошо! Вот что я имел в виду под словом «странно / неожиданно» в своем комментарии к вашему комментарию к моему ответу. Но следует ли этого ожидать? В документации говорится: в состоянии {$ A8} или {$ A +} поля в типах записей, объявленных без модификатора Packaging, и поля в структурах классов выравниваются по границам четвертого слова. - person Sertac Akyuz; 09.07.2010
comment
@Sertac: выравнивание четырех слов применяется только к типам данных длиной не менее 8 байт. В архитектуре x86 мало что можно получить, поместив байтовое поле на 8-байтовую границу. (Это могло бы помочь с конфликтами строк кеша в кэше L2, но это действительно незначительно). Как сказал Серг, типы данных выравниваются по своей естественной границе (= размеру данных) до $ A / n / size. Обратите внимание, что необходимо учитывать несколько точек выравнивания: смещение полей от начала структуры, выравнивание данных в массивах и заполнение самой записи для правильного выравнивания, когда она используется в массиве. - person dthorpe; 09.07.2010
comment
@dthorpe - Спасибо, все имеет смысл. Но я бы хотел, чтобы тема «структурированные типы» или «выравнивание полей» в документации была более ясной по этому поводу, особенно для тех, кто вроде меня не знает базовой архитектуры .. - person Sertac Akyuz; 09.07.2010