Bitwise operators were included in the C language for convenient low-level work with data and hardware: configuring registers, masking, multiplying, and dividing by powers of two. The rules for their operation were established during the era of 8-bit and 16-bit processors.
It is often easy to make mistakes with signed types during shifts, as the result depends on the implementation (arithmetic or logical shift), as well as when exceeding the size of the type. Errors can result in data corruption, incorrect calculations, and undefined behavior.
Left Shift (<<): equivalent to multiplying the value by 2 raised to the power of k (a << k). Always fills zeros on the right.
Right Shift (>>): for unsigned values, fills on the left with zeros (logical shift), and for signed - can fill either with the sign bit (arithmetic shift) or with zeros (behavior depends on the compiler).
Example:
unsigned int x = 5; // 0000 0101 unsigned int y = x << 1; // 0000 1010 == 10 int z = -4; // 1111 1100 (if 8 bits) int w = z >> 1; // May stay 1111 1110 (-2) or 0111 1110 (depends on the implementation)
Key features:
What happens when shifting a negative number to the right using >>?
The result depends on the implementation: most often it is an arithmetic shift preserving the sign, but the standard does not guarantee it!
What is the result of shifting by a number of bits greater than the type's bit-width?
Undefined behavior. For example, 1 << 32 for a 32-bit type could yield anything or even crash the program.
Can bitwise operators be used for floating-point numbers?
No, standard types float, double do not support bitwise operations. Only integer types.
A programmer shifted an int by 32 to form a mask - on some platforms this resulted in zero, while on others - an unrecognized value.
Pros:
Cons:
Instead, unsigned values and bit masking were used with macros, with clear documentation and type length checks through sizeof.
Pros:
Cons: