#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: Нет, но могут быть специфические для компилятора прагмы или атрибуты.