ProgrammingLow-level Software Developer, SI/Embedded C Developer

Describe how the rotary shift (circular shift) operator is implemented in C. Why is there no standard operator for this operation in C, and how can a safe circular shift for integers of any size be implemented?

Pass interviews with Hintsage AI assistant

Answer.

The rotary (circular) shift operation is the shifting of the bits of a number by a certain number of positions, with the "dropped" bits wrapping around to the opposite end. In C, there is no built-in operator for this task; this solution is historically related to the portability of the standard library and the necessity to explicitly define behavior for different platforms.

The problem is that the standard shift operators (<<, >>) do not perform a circular shift: they merely shift bits, replacing the "dropped" bits with zeros. To perform a circular shift, one must explicitly combine the results of two shifts and mask the result with a specified number of bits.

The solution is to implement a circular shift manually. For a 32-bit unsigned integer, it looks like this:

uint32_t rotate_left(uint32_t value, unsigned int shift) { return (value << shift) | (value >> (32 - shift)); } uint32_t rotate_right(uint32_t value, unsigned int shift) { return (value >> shift) | (value << (32 - shift)); }

Key points:

  • There is no built-in rotary shift operator in C.
  • One must manually combine two shifts and apply a mask.
  • The size of the data type (bitness) should be considered for portability.

Trick questions.

Can the << or >> operator perform a circular shift without additional operations?

No. Regular shifts replace bits that go out of bounds with zeros instead of wrapping them around to the other side.

What happens if a shift is performed equal to the size of the type (shift equals the number of bits in the number)?

The behavior is undefined (Undefined Behavior according to the C standard), always perform shifts modulo the size of the type.

Is it safe to perform rotary shifts with these functions for signed types?

No, always use unsigned types, as bitwise shifts for signed types behave differently depending on the compiler and architecture.

Common mistakes and anti-patterns

  • Using signed types instead of unsigned for bit shifts.
  • Not considering type size (e.g., 32 vs. 64 bits).
  • Missing shift masking (e.g., not doing shift = shift % 32 for 32-bit numbers).

Real-life example

Negative case

Using a regular shift for circular shifting:

uint32_t x = 0xFA3C0F00; uint32_t y = x << 5; // Not circular

Pros:

  • Simplicity of writing.

Cons:

  • Bits are lost, behavior does not meet expectations, difficult-to-catch bugs in cryptographic programs and data processing.

Positive case

Using a manual rotary shift function considering type size and protecting against UB:

uint32_t x = 0xFA3C0F00; uint32_t y = rotate_left(x, 5);

Pros:

  • Correct result on all platforms, protection against undefined behavior.

Cons:

  • Slightly more logical complexity, longer code.