Bitwise operators include the complement operator ~, bitwise shift operators >> and <<, bitwise AND operator &, bitwise exclusive OR operator ^, and bitwise inclusive OR operator | and compound assignment operators >>=, <<=, &=, ^= and |=. Bitwise operators should be used only with unsigned integer operands, as the results of some bitwise operations on signed integers are implementation-defined (Annex G.3.5 [ISO/IEC 9899:1990]).
The C90 Standard, 6.3, paragraph 4 [ISO/IEC 9899:1990], states:
Some operators (the unary operator ~ , and the binary operators <<, >>, &, ^, and |, collectively described as bitwise operators) shall have operands that have integral type. These operators return values that depend on the internal representations of integers, and thus have implementation-defined aspects for signed types.
Further, bitwise shift operator << for signed type is not defined in C90 subclause 6.3.7 [ISO/IEC 9899:1990].
Implementation details
The Microsoft C compiler documentation says that:
Bitwise operations on signed integers work the same as bitwise operations on unsigned integers.
On-line GCC documentation about the implementation of some bitwise operations on signed integers says:
Bitwise operators act on the representation of the value including both the sign and value bits, where the sign bit is considered immediately above the highest-value value bit.
Noncompliant Code Example (Right Shift)
...
Also, consider using the sprintf_s() function, defined in ISO/IEC TR 24731-1, instead of snprintf() to provide some additional checks. (See STR07-C. Use the bounds-checking interfaces for string manipulation.)
Noncompliant Code Example (enum)
In this noncompliant code example, initializer expressions for all enumeration constants in enum attrib_mask are unsigned integers. However, the C Standard says (section 6.1.3.3 [ISO/IEC 9899:1990]) that enumeration constants have type int. The bitwise OR is applied to signed integers which is implementation-defined behavior.
| Code Block | ||||
|---|---|---|---|---|
| ||||
enum attrib_mask { POINT_BIT = 0x02U, LINE_BIT = 0x04U }; unsigned int mask = (POINT_BIT | LINE_BIT); |
Compliant Solution (enum)
One solution is to cast the enumeration constants to an unsigned type to eliminate any possible implementation-defined behavior. The initializer expressions and enumeration constants have type int:
| Code Block | ||||
|---|---|---|---|---|
| ||||
enum attrib_mask
{
POINT_BIT = (1 << 1),
LINE_BIT = (1 << 2)
};
unsigned int mask = ((unsigned int)POINT_BIT | (unsigned int)LINE_BIT); |
Exceptions
INT13-EX1: 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.
...