Компоновка памяти набора - PullRequest
7 голосов
/ 06 июня 2011

Как организовано set в памяти в Delphi?

Я пытаюсь привести простой тип к типу набора, например

var
  MyNumber : Word;
  ShiftState : TShiftState;
begin
  MyNumber:=42;
  ShiftState:=TShiftState(MyNumber);
end;

Delphi (2009) выиграноне позволяю, и я не понимаю, почему.Это сделало бы мою жизнь намного проще в тех случаях, когда я получаю число, в котором отдельные биты кодируют разные значения перечисления, и я просто могу привести его следующим образом.Можно ли это сделать?

Один из подходов, который я собирался использовать, это:

var
  ShiftState : TShiftState;
  MyNumber : Word absolute ShiftState;
begin
  MyNumber:=42;
end;

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

Ответы [ 4 ]

6 голосов
/ 06 июня 2011

Набор Delphi - это битовое поле, биты которого соответствуют связанным значениям элементов в вашем наборе. Для набора нормальных перечислимых типов структура битов проста:

  • бит 0 соответствует заданному элементу с порядковым значением 0
  • бит 1 соответствует заданному элементу с порядковым значением 1
  • и т. Д.

Вещи становятся немного интереснее, когда вы имеете дело с несмежным набором или с набором, который не начинается с 0. Вы можете сделать это, используя тип поддиапазона Delphi (пример: set of 3..7) или используя перечисляемые типы которые указывают фактическое порядковое значение для элементов:

type enum=(seven=7, eight=8, eleven=11); 
EnumSet = set of enum;

В таких случаях Delphi выделит минимально необходимое количество байтов, которое будет включать все требуемые биты, но не будет «сдвигать» битовые значения, чтобы использовать меньше места. В примере EnumSet Delphi будет использовать два байта:

  • Первый байт будет иметь свой 7-й бит, связанный с seven
  • Второй байт будет иметь бит 0, связанный с eight
  • Второй байт будет иметь бит 3, связанный с eleven

Вы можете увидеть некоторые тесты, которые я провел здесь: Delphi 2009 - Ошибка? Добавление предположительно недопустимых значений в набор

Тесты проводились с использованием Delphi 2010, но не повторялись для Delphi XE.

1 голос
/ 06 июня 2011

К сожалению, я только сейчас наткнулся на следующий вопрос: Delphi 2009 - Ошибка?Добавление предположительно неверных значений в набор

Принятый ответ Cosmin содержит очень подробное описание того, что происходит с наборами в Delphi.И почему я лучше не использовал свой подход с absolute.Очевидно, переменная set может занимать от 1 до 32 байт памяти, в зависимости от значений перечисления.

1 голос
/ 06 июня 2011

Вы должны выбрать порядковый тип правильного размера. Для меня (D2007) ваш код компилируется с MyNumber: Byte:

procedure Test;
var
  MyNumber: Byte;
  ShiftState: TShiftState;
begin
  MyNumber := 42;
  ShiftState := TShiftState(MyNumber);
end;

Я использовал эту технику в некоторых ситуациях и не сталкивался с проблемами.


UPDATE

Тип TShiftState был расширен с Delphi 2010 и теперь включает два новых состояния ssTouch и ssPen на основе соответствующей страницы документа ( текущей страницы документа ). Delphi 2009 doc по-прежнему имеет TShiftState, определенный как набор из 7 состояний.

Итак, ваша попытка конвертировать Word в TShiftState сработает в Delphi 2010+, но Byte - правильный размер для Delphi 2009.

0 голосов
/ 07 июня 2011

Я использую это:

Для <= 8 элементов: PByte (@MyNumber) ^, для <= 16 элементов, PWord (@MyNumber) ^ и т. Д. </p>

Если перечисление занимает больше места (через опцию компилятора минимального размера), оно все равно будет работать.

...