c++标准库早期未提供rol/ror函数,因硬件行为不一致且逻辑移位语义与循环移位冲突;c++20起通过头文件引入std::rotl/std::ror,支持无符号整型并自动处理位宽和模运算,但需注意编译器版本兼容性及移位数范围限制。

为什么C++标准库没有rol和ror函数
C++标准不提供内置的循环移位(rotate left/right)运算符或函数,因为位移操作在不同硬件上行为不一致(比如x86有rol/ror指令,ARM则需多步模拟),且和<code>>>被定义为逻辑/算术移位,丢弃溢出位——这和循环移位“把移出的位补到另一端”的语义本质冲突。
所以你得自己写,或者用编译器内置函数(如__builtin_rotl32),但后者不是标准C++,跨平台时要小心。
用位运算手写rotl和ror的通用写法
核心思路:把左移后“掉出去”的高位,再用右移补回低位;反之亦然。关键是要避免对移位数取模时除零或未定义行为(比如移位数等于位宽)。
-
n是待移位的整数,shift是移位量,bits是类型总位数(如sizeof(int)*8) - 先做
shift %= bits,但必须确保bits > 0且shift非负;若shift可能为负,需先归一化 - 对于32位无符号整数:
uint32_t rotl32(uint32_t n, int shift) { shift &= 31; // 比 %32 更快,且避免负数问题 return (n > (32 - shift)); } (n 把高位左移出去,<code>(n >> (32 - shift))把原高位右移到低位——两者|就拼成循环结果- 注意:必须用无符号类型!否则
>>可能是算术右移,高位补1,结果错
用<bit></bit>头文件里的std::rotl/std::ror(C++20起)
C++20正式引入<bit></bit>,提供了std::rotl和std::ror模板函数,支持unsigned char到unsigned long long,自动处理位宽和移位模运算。
- 它们要求
shift在[0, bit_width)范围内,超出会UB;实际使用前建议手动截断:shift %= std::numeric_limits<t>::digits</t> - 示例:
#include <bit> #include <cstdint> uint32_t x = 0b1001; auto y = std::rotl(x, 2); // 结果是 0b0110</cstdint></bit>
- 比手写更安全:内部已处理
shift == 0、shift == bits等边界,且对每个整型特化做了优化 - 但注意:MSVC 19.30+、GCC 11+、Clang 12+才完全支持;老版本编译会报错
容易踩的坑:移位数超范围、符号类型、编译器优化干扰
移位操作在C++里对超范围移位(如1 )是未定义行为(UB),不是简单归零——哪怕你写了<code>shift %= 32,如果原始shift是负数且没预处理,%结果可能仍为负(取决于编译器),导致右移负位数,直接UB。
- 永远用
&代替%做模幂运算(仅适用于位宽是2的幂):shift &= 31比shift %= 32更快,且保证非负 - 别对
int或char直接移位——先转成unsigned,例如static_cast<unsigned char>(c)</unsigned> - 某些编译器(如GCC)在-O2下会把符合模式的手写
rotl内联成单条rol指令,但前提是表达式不能含分支或函数调用;加[[gnu::always_inline]]也没用,得靠模式匹配 - 调试时打印中间值?记得用
std::bitset看二进制,别只看十进制——0b10000000 是<code>0还是0x100,一眼就能验出来
循环移位看着简单,但每一步都卡在类型、范围、符号、平台四个点上;写完务必用边界值测:0、全1、shift=0、shift=bits-1、shift=bits。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











