Битовые маски в C - PullRequest
       47

Битовые маски в C

5 голосов
/ 11 октября 2009

Есть ли в C переносимый способ узнать маску для битового поля во время компиляции?

В идеале, я хотел бы иметь возможность атомарно очистить поле вроде этого:

struct Reference {
    unsigned age : 3;
    unsigned marked : 1;
    unsigned references : 4;
};

struct Reference myRef;
__sync_and_and_fetch(&myRef, age, ~AGE_MASK);

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

Ответы [ 5 ]

2 голосов
/ 11 октября 2009

Или, если вы действительно хотели маску:

union Reference {
  unsigned asWord;
  struct {
    unsigned age : 3;
    unsigned marked : 1;
    unsigned references : 4;
  } asFields;
}

Reference agemask_ref;
agemask_ref.asFields = (typeof(agemask_ref.asFields)){0, -1, -1};
unsigned agemask = agemask_ref.asWord;
2 голосов
/ 11 октября 2009

Вы можете сделать что-то вроде:

union Reference {
  unsigned asWord;
  struct {
    unsigned age : 3;
    unsigned marked : 1;
    unsigned references : 4;
  } asFields;
}

Чтобы атомарно очистить поле myRef, выполните

union Reference myRef;

union Reference oldr = myRef;
union Reference newr = oldr;
newr.asFields.age = 0;
compare_and_swap(&myRef.asWord, oldr.asWord, newr.asWord);

(+ непоказанный код для обработки в случае сбоя Compare_and_swap)

2 голосов
/ 11 октября 2009

Я не знаю, как это сделать во время компиляции, но во время выполнения это должно быть простым делом объединения экземпляра вашей структуры битового поля с неподписанным целым числом соответствующего размера и установки всех полей в 0, кроме тот, который вас интересует, который должен быть установлен на все 1 - значение unsigned int будет битовой маской, которую вы хотите. Вы можете сделать это для каждого поля при запуске, возможно, с помощью макроса, чтобы избежать повторяющегося кода. Разве этого не достаточно?

1 голос
/ 11 октября 2009

Я не думаю, что это возможно - даже с помощью offsetof (), который работает для смещения байтов, но, похоже, не работает для битовых полей. Я бы переопределил поля как enums / define (0x01 0x02 и т. Д.) И управлял бы битами самостоятельно, чтобы вы могли получить ваши атомарные изменения.

0 голосов
/ 11 октября 2009

Да, это можно сделать. Вам необходимо зафиксировать значение и выполнить операцию. Затем вам нужно использовать атомарное сравнение и обмен (например, InterlockedCompareExchange в Windows), чтобы сохранить новое значение, если память все еще содержит старое значение. Если кто-то изменил значение, вы зациклились и попробуйте снова. Обратите внимание, что это стандартный шаблон для выполнения любой операции над частью данных размером в слово, где встроенная функция недоступна.

В приведенном ниже коде используется int - как указал Кит, вы можете использовать объединение, чтобы иметь возможность получить значения структуры как int.

int oldValue, newValue;
do
{
    oldValue = myRef;
    newValue = oldValue & ~AGE_MASK;
} while (InterlockedCompareExchange(&myRef, newValue, oldValue) != oldValue);
...