Cosmic M93
Cosmic M93

Reputation: 23

Macro values defined using bit-shifts

I've been going through an old source project, trying to make it compile and run (it's an old game that's been uploaded to GitHub). I think a lot of the code was written with C-style/C-syntax in mind (a lot of typedef struct {...} and the likes) and I've been noticing that they define certain macros with the following style:

#define MyMacroOne (1<<0) //This equals 1
#define MyMacroTwo (1<<1) //This equals 2, etc.

So my question now is this - is there any reason why macros would be defined this way? Because, for example, 0x01 and 0x02 are the numerical result of the above. Or is it that the system will not read MyMacroOne = 0x01 but rather as a "shift object" with the value (1<<0)?


EDIT: Thanks for all of your inputs!

Upvotes: 2

Views: 3865

Answers (5)

VonC
VonC

Reputation: 1323793

To illustrate how this (1<<0) syntax is more practical, consider this example from the code-base of Git 2.25 (Q1 2020), which moves the definition of a set of bitmask constants from 0ctal literal to (1U<<count) notation.

See commit 8679577 (17 Oct 2019) by Hariom Verma (harry-hov).
(Merged by Junio C Hamano -- gitster -- in commit 8f40d89, 10 Nov 2019)

builtin/blame.c: constants into bit shift format

Signed-off-by: Hariom Verma

We are looking at bitfield constants, and elsewhere in the Git source code, such cases are handled via bit shift operators rather than octal numbers, which also makes it easier to spot holes in the range.

If, say, 1<<5 was missing:

  • it is easier to spot it between 1<<4 and 1<<6
  • than it is to spot a missing 040 between a 020 and a 0100.

So instead of:

#define OUTPUT_ANNOTATE_COMPAT  001
#define OUTPUT_LONG_OBJECT_NAME 002
#define OUTPUT_RAW_TIMESTAMP    004
#define OUTPUT_PORCELAIN        010

You get:

#define OUTPUT_ANNOTATE_COMPAT      (1U<<0)
#define OUTPUT_LONG_OBJECT_NAME     (1U<<1)
#define OUTPUT_RAW_TIMESTAMP        (1U<<2)
#define OUTPUT_PORCELAIN            (1U<<3)

Upvotes: 0

Petr Skocik
Petr Skocik

Reputation: 60058

You can always use constant integer expression shifts as a way to express (multiples of) powers of two, i.e. Multiple*(2 to the N-th power) = Mutliple << N (with some caveats related to when you hit the guaranteed size limits of the integer types and UB sets in*) and pretty much rely on the compiler folding them.

An integer expression made of integer constants is defined as an integer constant expression. These can be used to specify array sizes, case labels and stuff like that and so every compiler has to be able to fold them into a single intermediate and it'd be stupid not to utilize this ability even where it isn't strictly required.


*E.g.: you can do 1U<<15, but at 16 you should switch to at least 1L<<16 because ints/unsigneds are only required to have at least 16 bits and leftshifting an integer by its width or into the place where its sign bit is is undefined (6.5.7p4):

The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1 x 2E2 , reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1 x 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

Upvotes: 0

user694733
user694733

Reputation: 16043

It makes it more intuitive and less error prone to define bit values, especially on multibit bitfields. For example, compare

#define POWER_ON     (1u << 0)
#define LIGHT_ON     (1u << 1)
#define MOTOR_ON     (1u << 2)
#define SPEED_STOP   (0u << 3)
#define SPEED_SLOW   (1u << 3)
#define SPEED_FAST   (2u << 3)
#define SPEED_FULL   (3u << 3)
#define LOCK_ON      (1u << 5)

and

#define POWER_ON     0x01
#define LIGHT_ON     0x02
#define MOTOR_ON     0x04
#define SPEED_STOP   0x00
#define SPEED_SLOW   0x08
#define SPEED_FAST   0x10
#define SPEED_FULL   0x18
#define LOCK_ON      0x20

Upvotes: 2

0___________
0___________

Reputation: 67476

It is convenient for the humans

for example

#define PIN0 (1u<<0)
#define PIN5 (1u<<5)

#define PIN0MASK (~(1u<<0))
#define PIN5MASK (~(1u<<5))

and it is easy too see if there is a correct bit position. it does not make the code slower as it is calculated at the compile time

Upvotes: 1

code707
code707

Reputation: 1701

Macro are just replacement text. Everywhere macro is replaced by replacement text!! This is convenient especially if you want to name something constant which otherwise is prone to mistakes.

Upvotes: 0

Related Questions