2010-07-08 2 views
8

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

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

+0

Я знаю, что выравнивание байтов по умолчанию для записей Delphi изменилось в последней версии (думаю, 2009), но я не уверен в деталях. –

ответ

2

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

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

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

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

+0

Как определить «выравнивание»? –

+0

Найдено: {$ A4}. Но это не помогло. Число теперь 464. Я пробовал все другие возможности {$ A?}, И ни один из них не совпадал. –

14

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

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

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

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

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

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

+4

Все это предполагает, что у вас нет возможности вернуться к D7 и изменить код там, чтобы упаковать структуру записи и записать файл с упакованными записями. Это будет меньше работы, чем при попытке сопоставить байтов после факта. – dthorpe

+0

Hind sight всегда 20/20 ... и развитие мышления вперед (и разработчиков) не всегда происходит. Хорошая идея о упакованной записи с пробелами на 2009/2010 год. Мы попробуем. –

+8

Оглядываясь назад, тоже должно быть образовательным. Написание распакованных записей в файл должно происходить только один раз за карьеру. ;> – dthorpe

7

Я считаю, что вы нанесли функцию!. Я не мог получить разумный размер с вашим 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; 

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

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

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

является (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 вы либо объявляете каждое поле - без группировки, либо изменение поведения. В любом случае pack ваша запись и добавьте поле фиктивного массива 6 байтов в конец, и вам должно быть хорошо идти.

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

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

+0

Размер второй записи в Delphi 2009 составляет 128 байт – kludg

+0

@Serg - спасибо, что посмотрели, так что это не особенность. На самом деле я столкнулся с другими странными/неожиданными выравниваниями во время тестирования с D2007. Я рад, что более поздние версии имеют это прямо! –

2

Применение директивы {$ 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 - читать Sertaç AKYÜZ ответ и мой комментарий к ней

+0

А, Ок! Вот что я имел в виду под «странным/неожиданным» с моим комментарием к вашему комментарию моего ответа. Но можно ли это ожидать? Документы говорят: «В состоянии {$ A8} или {$ A +} поля в типах записей, объявленных без наложенного модификатора и полей в структурах классов, выровнены на четырехзначных границах слова." –

+2

@Sertac: выравнивание четырех слов применяется только к типам данных длиной не менее 8 байтов. В архитектуре x86 мало что можно получить, поставив байтовое поле на границу 8 байтов. (Это может помочь при столкновении строк кэша в кэше L2, но это действительно очень незначительно). Как сказал Серг, типы данных совпадают с их естественной границей (= размер данных) до размера $ A/n /. Обратите внимание, что есть несколько точек выравнивания, которые следует учитывать: смещение полей от начала структуры, выравнивание данных внутри массивов и заполнение самой записи, чтобы хорошо выравнивать, когда она используется в массиве. – dthorpe

+1

@dthorpe - Спасибо, все имеет смысл. Но я желаю, чтобы тема «структурированные типы» или «выравнивание полей» в документации была более явной, особенно для тех, которые мне не знакомы с базовой архитектурой. –

3

Я знаю, что это старый пост, но я столкнулся с той же проблемой с Delphi XE2 вчера , У меня есть несколько типизированных файлов, которые были выписаны с помощью приложения Turbo Delphi 2006, и я также сделал ошибку, не используя Packed records.Моя ситуация была усугублена тем, что я использовал записи с переменной длиной (это то, что мы называем их в мире 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. В следующий раз я буду использовать Packed records (или, еще лучше, SQLite). Но я думал, что поделюсь этим, так как этот сайт помог мне неизмеримо годами.

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

Смежные вопросы