Which one is to use?
typedef enum
{
item_1 = 1,
item_2 = 5,
item_2 = 8,
item_2 = 10,
} formA_typeDef;
or
typedef enum
{
item_1 = (1),
item_2 = (5),
item_2 = (8),
item_2 = (10),
} formB_typeDef;
I personally think that the first, A_typeDef;
is the adequate format to use, unless the value to assign to the item isn't a simple integer, but a combination of #define
such as:
#define REG1 0xFFF7U
#define REG2 0xFFFFU
typedef enum
{
item_1 = (REG1 | REG2 | 0x00U),
item_2 = (REG1 | REG2 | 0x01U),
item_2 = (REG1 | REG2 | 0x02U),
item_2 = (REG1 | REG2 | 0x03U),
} formC_typeDef;
CodePudding user response:
There is no reason to use redundant parentheses around the initializer expression for an enum
constant. The first definition is fine and readable.
Conversely, if you were to define the constants as macros, you would make sure so use parentheses to avoid precedence errors when the macro is replaced with its expansion in the final expressions.
Using enums (no parentheses needed):
#define REG1 0xFFF7U
#define REG2 0xFF00U
typedef enum {
ITEM_1 = REG1 | REG2 | 0x00U,
ITEM_2 = REG1 | REG2 | 0x01U,
ITEM_3 = REG1 | REG2 | 0x02U,
ITEM_4 = REG1 | REG2 | 0x03U,
} formC_typeDef;
Using macros (parentheses required):
#define ITEM_1 (REG1 | REG2 | 0x00U)
#define ITEM_2 (REG1 | REG2 | 0x01U)
#define ITEM_3 (REG1 | REG2 | 0x02U)
#define ITEM_4 (REG1 | REG2 | 0x03U)
CodePudding user response:
Is it required
No.
Is it preferred
No.
Which one is to use?
The former, because there exist no sensible arguments to use the latter version.
As a side note, using enum
to store bit masks is a horrible idea since enumeration variables could get any signedness, and enumeration constants (item_1
) are guaranteed to be signed. And you don't want bit masks to have a signed type, as that could lead to subtle bugs elsewhere.
And that would be the same reason as why we should always end hex constants with u
/U
: #define REG1 0xFFF7u
CodePudding user response:
The convention of putting brackets round everything is very much a defence against badly written macros. Stop using macros, and you stop having to be so defensive.
In the case of #define REG2
you might actually want to place brackets round the usage of (REG2)
where REG2 is actually defined as an operation which unfortunately uses operations of lower precedence and the brackets have been forgotten - the language does not mandate the use of brackets in macros - and causes the outer expression to be interpreted differently. Of course a house rule that mandates all macro definitions are bracketed also helps.
In your case the only useful operation would be ?:
, as all other operations are higher precedence than |
Imagine if #define REG2 COND2 ? 0x1200 : 0xFF00
Without any brackets, REG1 | REG2 | 0x02U
would be interpreted as:
REG1 | COND2 ? 0x1200U : 0xFF00U | 0x02U
Which is actually
(REG1 | COND2) ? 0x1200U : (0xFF00U | 0x02U)
This would have the surprising effect of almost always applying 0x1200, effectively ignoring COND2 and the last argument.
By choosing |
in your example it makes it quite difficult to generate a realistic example. A more realistic one is when ANDing with a mask:
#define MASK1 0x00F0U|0x000F
unsigned v = 0x1234U & MASK1;
Is expanded as
unsigned v = 0x1234U & 0x00F0U|0x000F;
But by precedence rules
unsigned v = (0x1234 & 0x00F0U) | 0x000F;
For the surprising answer 0x003F
So, as a belts-and-braces rule, every "value" macro you define should have surrounding brackets, and every usage of a macro you don't trust/know should be bracketed. The following will never be misinterpreted, but does look ugly:
#define MASK1 (0x00F0U|0x000F)
unsigned v = 0x1234U & (MASK1);
Also, don't ignore the compiler warnings when the compile thinks you don't know the precedence rules, but they won't always save you.
If you have absolute confidence that the first rule was followed, or you have an IDE that lets you see the definitions, then maybe you don't need to be so careful when using macro values. Of course, someone could come along in the future and change a definition into an unsafe one, but that should be spotted in code review. You might upgrade an external library header. So how trusting are you?
Alternatively, stop using macros, which probably means using C 11 typed enums instead.