некорректный размер структуры при использовании в записях Variant Parts

У меня есть проект DLL, который предоставляет определение типа с "вариантной частью" Delphi следующим образом:

type TValue = record 
case Kind : cardinal of 
valueShortCard : ( ValShortCard : byte ); 
valueLongReal : ( ValLongReal : double ); 
end; 

Я ожидаю, что эта структура будет иметь размер 12 байтов (4Б для cardinal плюс 8B для double, поскольку это больший из этих двух типов).

Но если у меня есть приложение и я вызываю функцию sizeof () для структуры в DLL, в нем указано, что размер равен 16 байтам.

Кроме того, если я объявлю ту же структуру непосредственно в проекте приложения .exe, sizeof () вернет правильный размер, равный 12 байтам.

Если я запускаю приложение и пытаюсь получить значение ValLongReal из скомпилированной библиотеки DLL, возвращаемое значение неверно и кажется, что оно сдвинуто на 4 байта в памяти.

I.E. если «double» переменная в DLL содержит значение «40 45 9a e1 47 ae 14 7b» (интерпретируется в байтовых значениях), значение, возвращаемое приложением после чтения его из DLL, будет «47 ae 14 7b 77 07. 06 7д ". Это означает, что имеется 4 байта перекрытия правильных данных, а последние 4 байта неверны и считываются из следующего пространства в памяти.

Примечание. Такое поведение уже наблюдалось в Delphi XE4, мы только что обновились до XE5, но оно ведет себя точно так же. Используемая ОС - Win7 32-битная.

спасибо за ваши предложения


person ethcz    schedule 13.09.2013    source источник
comment
Зависит от настроек компилятора для выравнивания.   -  person LU RD    schedule 13.09.2013


Ответы (1)


Определите это как таковое:

type TValue = packed record 
  case Kind : cardinal of 
  valueShortCard : ( ValShortCard : byte ); 
  valueLongReal : ( ValLongReal : double ); 
  end; 

Вот, sizeof(TValue)=12, как ты и ожидал.

Добавление packed record не приводит к генерации выравнивания. Если выравнивания нет, double будет выровнен по 8-байтовым границам, поэтому Kind будет использовать 8 байтов вместо 4, а valueLongReal также, следовательно, sizeof(TValue)=8+8=16 в вашем случае.

Обратите внимание, что в AMD / Intel вам лучше выровнять свои double переменные по соображениям производительности. На ARM требуется выравнивание.

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

person Arnaud Bouchez    schedule 13.09.2013
comment
А на x64 дублёры выравнивать надо? Я не думал, что компилятор выдает инструкции SSE, требующие выравнивания. Вам нужно выровнять их, если вы используете библиотеки, такие как GR32, которые используют выровненные инструкции. Я также думаю, что ответ, возможно, немного вводит в заблуждение. Из первого предложения похоже, что вы защищаете упаковку. Я знаю, что ты бы никогда этого не сделал. - person David Heffernan; 13.09.2013
comment
Это не вызовет исключения ЦП, поскольку в архитектуре x64 исключения выравнивания отключены по умолчанию, а исправления выполняются аппаратным обеспечением. Но это будет выполняться намного медленнее. По умолчанию компилятор пытается выровнять каждое двойное значение, и мы всегда должны его придерживаться! Таким образом, вам необходимо согласоваться по причинам производительности и согласованности. - person Arnaud Bouchez; 15.09.2013
comment
То же самое и с x86, заметьте. Неправильно выровненные двойники, и это кошмар производительности. В старых версиях Delphi это было сделано путем выравнивания по границам не более 4 байтов. - person David Heffernan; 15.09.2013
comment
обновление: смещенные двойники гораздо меньше влияют на производительность с новейшими процессорами Intel и AMD. Но выравнивание по-прежнему требуется для ARM, IIRC. - person Arnaud Bouchez; 22.08.2020