In C and C++, the expression some_num & 0xABCD == 5
will effectively evaluate as some_num & (0xABCD == 5)
. This is unlike all of the standard arithmetic operators, as they have higher precedence than the comparison operators, so some_num + 5 == 5
will evaluate as we expect.
Why is this? It seems very counterintuitive that the standard arithmetic operators have a much higher precedence than the bitwise arithmetic operators.
This is a historical artifact of the language.
Early versions of C didn't have the ||
and &&
operators, so the |
and &
operators were used for logical AND and logical OR in cases where the operands had values of either 0 or 1.
As a result, these operators were given lower precedence to fit into this use case.
When the ||
and &&
operators were later added around 1972, the precedence of |
and &
remained lower than that of arithmetic and relational operators to avoid breaking existing code, and that decision persists to today.
From Dennis Ritchie's paper, The Development of the C Language:
Rapid changes continued after the language had been named, for example the introduction of the && and || operators. In BCPL and B, the evaluation of expressions depends on context: within if and other conditional statements that compare an expression's value with zero, these languages place a special interpretation on the and (&) and or (|) operators. In ordinary contexts, they operate bitwise, but in the B statement
if (e1 & e2) ...
the compiler must evaluate e1 and if it is non-zero, evaluate e2, and if it too is non-zero, elaborate the statement dependent on the if. The requirement descends recursively on & and | operators within e1 and e2. The short-circuit semantics of the Boolean operators in such `truth-value' context seemed desirable, but the overloading of the operators was difficult to explain and use. At the suggestion of Alan Snyder, I introduced the && and || operators to make the mechanism more explicit.
Their tardy introduction explains an infelicity of C's precedence rules. In B one writes
if (a==b & c) ...
to check whether a equals b and c is non-zero; in such a conditional expression it is better that & have lower precedence than ==. In converting from B to C, one wants to replace & by && in such a statement; to make the conversion less painful, we decided to keep the precedence of the & operator the same relative to ==, and merely split the precedence of && slightly from &. Today, it seems that it would have been preferable to move the relative precedences of & and ==, and thereby simplify a common C idiom: to test a masked value against another value, one must write
if ((a&mask) == b) ...
where the inner parentheses are required but easily forgotten.