битовые поля против стандартных определений

#c #bit-fields #stdint

#c #битовые поля #стандартный код

Вопрос:

Итак, я программирую на C , и, насколько я могу судить, эквивалента stdint.h на C не существует. Что не проблема, поскольку вы можете просто взять копию stdint и включить ее… но мой вопрос в основном заключается в следующем,

в чем разница между этими двумя частями кода:

 struct FREQ{
    unsigned int FREQLOHI :16;
    //etc...

};
  

и

 struct FREQ{
    uint16_t FREQLOHI;
    //etc...
}
  

помимо очевидных ограничений битовых полей, существует ли проблема с производительностью / переносимостью? Что предпочтительнее?

Комментарии:

1. #include <cstdint> будет включать этот заголовок.

2. Вы не можете просто «захватить копию». Например, получение копии из вашего компилятора Visual Studio и помещение ее в компилятор mingw может привести к ошибочному поведению. Рядом с этим cstdint должен быть доступный заголовок начиная с C 11.

3. <cstdint>, насколько я знаю, не существует в 2008 или 2010 годах. @Killian, я работаю в Visual studio, и вы можете найти копию здесь: ссылка , которая соответствует стандарту ISO C9x stdint.h для Microsoft Visual Studio

4. Если в вашем компиляторе C отсутствует [stdint.h], используйте версию Boost.

5. Оба <cstdint> и <stdint.h> определенно поставляются с MSVC 2010.

Ответ №1:

Разница в том, что unsigned int может быть разного размера на разных платформах, в то время как uint16_t гарантированно имеет 16-битную ширину. Это означает, что экземпляр первой структуры (битового поля) может иметь разный размер на разных платформах. Кроме того, доступ к битовым полям обходится дороже, поскольку он требует дополнительного сдвига и маски.

Например, на ноутбуке, где unsigned int имеет 32-разрядную ширину, первая структура имеет 32-разрядную ширину, а вторая структура — 16-разрядная.

Когда дело доходит до переносимости, битовые поля находятся в гораздо более чистой ситуации, поскольку это старая функция языка C, которая была включена в C , когда он был стандартизирован в 1998 году (ISO / IEC 14882: 1998). С другой стороны, stdint.h был добавлен в C только в 1999 году (стандарт ISO / IEC 9899: 1999) и, следовательно, не является частью C 98 (ISO / IEC 14882: 1998). Затем соответствующий заголовок cstdint был включен в C TR1, но он поместил все идентификаторы в std::tr1 пространство имен. Boost также предлагал заголовок. Самый последний стандарт C (C 11, он же ISO / IEC 14882: 2011, который вышел в сентябре 2011 года) включает заголовок cstdint и помещает все идентификаторы в std пространство имен. Несмотря на это, cstdint широко поддерживается.

Комментарии:

1. На самом деле вы не обратили внимания на тот факт, что unsigned int является битовым полем.

Ответ №2:

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

 struct Bitfields
{
    unsigned int eight_bit : 8;
    unsigned int sixteen_bit : 16;
    unsigned int eight_bit_2 : 8;
};
  

Может быть упакован как

 0            8                        24
-----------------------------------------------------
| eight_bit  | sixteen_bit            | eight_bit_2 |
-----------------------------------------------------
  

Каждый раз, когда вы обращаетесь sixteen_bit к нему, происходит сдвиг и побитовая операция amp;.

С другой стороны, если вы делаете

 struct NonBitfields
{
    uint8_t eight_bit;
    uint16_t sixteen_bit;
    uint8_t eight_bit_2;
};
  

затем компилятор обычно выравнивает элементы по границам слов и представляет это как что-то вроде:

 0            8           16           24
-----------------------------------------------------
| eight_bit  |            | sixteen_bit             |
-----------------------------------------------------
| eight_bit_2|                                      |
-----------------------------------------------------
  

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


Вот некоторые другие различия:

  • Вы не можете применить sizeof к элементу битового поля.
  • Вы не можете передать элемент битового поля по ссылке.

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


Что касается предпочтений, я бы предпочел использовать uint16_t вместо битовых полей, если только нет веской причины для объединения полей вместе для экономии места. Если у меня много bool полей внутри структуры, я обычно использую битовые поля для сжатия этих логических флагов вместе в одном слове.

Комментарии:

1. Нет ли какого-нибудь ключевого слова, которое «запрашивает» компилятор для выравнивания по «скорости»?

2. @muntoo: Нет, но могут быть специфические для компилятора прагмы или атрибуты.