Most built-in bitwise operators (~
, >>
, >>=
, &
, &=
,
^
, ^=
, |
, and |=
) have implementation-dependent results when performed on signed operands, and
bitwise left shift (<<
and <<=
) has unspecified or undefined behavior when performed on negative operands.
Therefore, bitwise operations should not be performed on signed operands.
Starting with C++20, the behaviors have been defined more accurately (negative values have to be represented using two’s complement), and therefore
this rule will only report an issue when the second operand of a shift operator is signed (shifting by a negative value is still undefined
behavior).
Noncompliant code example
if ( ( uint16_a & int16_b ) == 0x1234U ) // Noncompliant until C++20
if ( ~int16_a == 0x1234U ) // Noncompliant until C++20
auto f(int i) {
return 1 << i; // Noncompliant
}
Compliant solution
if ( ( uint16_a | uint16_b ) == 0x1234U )
if ( ~uint16_a == 0x1234U )
auto f(unsigned int i) {
return 1 << i;
}
Exceptions
When used as bit flags, it is acceptable to use preprocessor macros as arguments to the & and | operators even if the value is not explicitly
declared as unsigned.
fd = open(file_name, UO_WRONLY | UO_CREAT | UO_EXCL | UO_TRUNC, 0600);
If the right-side operand to a shift operator is known at compile time, it is acceptable for the value to be represented with a signed type
provided it is positive.
#define SHIFT 24
foo = 15u >> SHIFT;
When combining several bitwise operations, even if all leaf operands are unsigned, if they are smaller than an int
, some intermediate
results will be of type signed int
, due to integral promotion. However, this situation is usually not an issue, and is an exception for
this rule:
unsigned int f(unsigned short src) {
return (src >> 3) & 0x1F; // (src >> 3) is of type signed int
}