Как уже говорили другие, ваша проблема (а) может быть решена с помощью <stdint.h>
и либо uint32_t
, либо uint_least32_t
(если вы хотите беспокоиться о мэйнфреймах Burroughs, которые имеют 36-битные слова). Обратите внимание, что MSVC не поддерживает C99, но @DigitalRoss показывает, где можно получить подходящий заголовок для использования с MSVC.
Ваша проблема (б) не является проблемой; C будет печатать конвертировать безопасно для вас, если это необходимо, но, вероятно, даже не нужно.
Наибольшую обеспокоенность вызывает (с) и, в частности, подполе формата. Там 3 значения действительны. Вы можете справиться с этим, выделив 3 бита и требуя, чтобы 3-битное поле было одним из значений 1, 2 или 4 (любое другое значение недопустимо из-за слишком большого или слишком малого количества установленных битов). Или вы можете выделить 2-битное число и указать, что 0 или 3 (или, если вы действительно хотите, один из 1 или 2) недопустимы. Первый подход использует еще один бит (в настоящее время это не проблема, так как вы используете только 20 из 32 бит), но это подход с чистым битовым флагом.
При написании вызовов функций особых проблем не возникает:
some_function(FORMAT_A | STORAGE_INTERNAL, ...);
Это будет работать независимо от того, является ли FORMAT_A #define или enum (если вы правильно указали значение enum). Вызываемый код должен проверить, была ли у вызывающего абонента ошибка в концентрации, и написал:
some_function(FORMAT_A | FORMAT_B, ...);
Но это внутренняя проверка модуля, о которой нужно беспокоиться, а не проверка пользователей модуля.
Если люди будут много переключать биты в элементе flags
, макросы для установки и отмены поля формата могут быть полезными. Некоторые могут утверждать, что любые чистые логические поля едва ли нуждаются в этом (и я бы посочувствовал). Возможно, лучше всего рассматривать элемент flags
как непрозрачный и предоставлять «функции» (или макросы) для получения или установки всех полей. Чем меньше людей могут ошибаться, тем меньше будет ошибок.
Подумайте, подходит ли вам использование битовых полей. Мой опыт показывает, что они приводят к большому коду и не обязательно к очень эффективному коду; YMMV.
Хммм ... пока ничего особенного.
- Я бы использовал перечисления для всего, потому что они гарантированно будут видны в отладчике, где значения #define отсутствуют.
- Я бы, вероятно, не предоставлял макросы для получения или установки битов, но иногда я жестокий человек.
- Я бы дал руководство о том, как установить форматную часть поля флагов, и мог бы предоставить макрос для этого.
Примерно так:
enum { ..., FORMAT_A = 0x0010, FORMAT_B = 0x0020, FORMAT_C = 0x0040, ... };
enum { FORMAT_MASK = FORMAT_A | FORMAT_B | FORMAT_C };
#define SET_FORMAT(flag, newval) (((flag) & ~FORMAT_MASK) | (newval))
#define GET_FORMAT(flag) ((flag) & FORMAT_MASK)
SET_FORMAT
безопасно, если используется точно, но ужасно при злоупотреблении. Одним из преимуществ макросов является то, что вы можете заменить их функцией, которая тщательно проверяет вещи при необходимости; это хорошо работает, если люди используют макросы последовательно.