Home > Enterprise >  Is it required and/or preferred to wrap C enum items in parenthesis , and if so, what's the uti
Is it required and/or preferred to wrap C enum items in parenthesis , and if so, what's the uti

Time:05-10

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.

  • Related