Подходят ли перечисления для масок?

Поскольку значения маски являются целыми числами, мы можем представлять их по-разному. Например, в качестве макросов:

#define ENABLE_FEATURE_A 0x1
#define ENABLE_FEATURE_B 0x2
#define ENABLE_FEATURE_C 0x4
#define ENABLE_FEATURE_D 0x8

Или перечисления:

typedef enum {
  FEATURE_A = 0x1,
  FEATURE_B = 0x2,
  FEATURE_C = 0x4,
  FEATURE_D = 0x8
} enabled_features_t

Я утверждаю, что представление перечисления не подходит для масок, поскольку маски предназначены для объединения (с побитовыми операциями). C требует приведения перечислений к int, поскольку от нас не ожидают выполнения операций над числами, которые могут не иметь значения. Поэтому я думаю, что использование макросов более подходит для флагов. Я слишком много об этом думаю?

В C перечисление — это, по сути, способ введения именованных символических констант int, как и в макросах. По сути, это простые int ценности. Итак, в вашем коде нет никакой разницы между макросом ENABLE_FEATURE_A и символом перечисления FEATURE_A.

Some programmer dude 10.07.2024 10:53

Перечислители являются целочисленными константами. Их не нужно кастовать.

n. m. could be an AI 10.07.2024 11:13

@Someprogrammerdude, что ты имеешь в виду под «простым int»? Они хранятся в памяти как int, но компилятор не указывает им явный тип?

polozer 10.07.2024 11:24

@polozer Перечисление вообще не хранится в памяти. Как я уже сказал, это символические константы int и, как и макросы, являются константами времени компиляции, которые заменяются компилятором. Итак, предполагая перечисление, определенное в вопросе, выполнение int features = FEATURE_A | FEATURE_B; будет скомпилировано как int features = 1 | 2;, а это именно то, чего, похоже, хочет ОП.

Some programmer dude 10.07.2024 11:27

Вы даже можете использовать FEATURE_AC = FEATURE_A | FEATURE_C, в своем списке перечисления...

Fe2O3 10.07.2024 11:39
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
70
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

В C «членом» перечисления является константа перечисления типа int, согласно C 2018 6.2.1 1:

…Член перечисления называется константой перечисления…

и 2018 6.4.4.3 2:

Идентификатор, объявленный как константа перечисления, имеет тип int.

Сам тип перечисления может иметь другой тип согласно C 6.7.2.2 4:

Каждый перечислимый тип должен быть совместим с char, целочисленным типом со знаком или целочисленным типом без знака. Выбор типа определяется реализацией,…

Я утверждаю, что представление перечисления не подходит для масок, поскольку маски предназначены для объединения (с побитовыми операциями).

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

Часто предпочтительнее использовать беззнаковый тип с побитовыми операциями из-за проблем с тем, как стандарт C определяет или не определяет вычисления, включающие знаковый бит в знаковых типах. Кроме того, значения int можно использовать в качестве битовых масок.

C требует приведения перечислений к int, поскольку от нас не ожидают выполнения операций над числами, которые могут не иметь значения.

В стандарте C такого требования нет. Возможно, вы думаете о некоторых проблемах, возникающих в C++?

Спасибо Эрик. Кажется, все согласны с тем, что в перечислениях флагов нет ничего плохого. Что касается приведения, я провел несколько экспериментов и не обнаружил никаких проблем с приведением в C или C++. Должно быть, я неправильно это запомнил.

Lolo 10.07.2024 13:42

@Lolo Согласовано, что их не следует использовать для этой цели. Авторитетные источники см., например, в правиле 10.1 MISRA C:2023.

Lundin 10.07.2024 14:45
Ответ принят как подходящий

Нет, константы перечисления не подходят для битовых масок по двум причинам:

  • Константы перечисления относятся к типу int, который является знаковым, поэтому его нежелательно смешивать, в частности, с побитовыми операциями, поскольку многие побитовые операции, такие как сдвиги, вызывают различные формы плохо определенного поведения при использовании со знаковыми типами.
  • Они слишком малы. Вы не можете поместить число 0x80000000 в константу перечисления, поэтому его практически невозможно использовать для битовой маскировки 32-битного числа.

На сегодняшний день подходящим решением (ISO C 9899:2018) является использование #define с целочисленными константами, оканчивающимися суффиксом U/u.


Однако будущий стандарт C23 решит эту проблему. Это станет возможным использовать:

typedef enum : uint32_t
{
  FEATURE_X = 0x80000000
} enabled_features_t;

Теперь эквивалентным типом, используемым для enum объектов этого типа, а также константы перечисления, является uint32_t.

Спасибо. Меня беспокоил кастинг, в котором я ошибался, но я не думал о проблеме int и uint и проблеме размера.

Lolo 11.07.2024 11:13

Другие вопросы по теме