应优先使用 n & 1 判断奇偶,因其对所有有符号整数均安全、无平台依赖、无除法开销,且编译器不保证 n % 2 总被优化为位运算。

用 % 判断奇偶是最直观但未必最优的方式
绝大多数人写 n % 2 == 0 判断偶数,n % 2 == 1 或 n % 2 != 0 判断奇数。这在语义上完全正确,编译器对整数常量模 2 通常会自动优化成位运算,但前提是:类型是无符号或非负有符号整数,且编译器能确认无溢出/负数取模行为不影响逻辑。
问题在于 C++ 中负数对 2 取模结果依赖实现:-3 % 2 可能是 -1(GCC/Clang),也可能是 1(某些平台),导致 n % 2 == 1 在负数时不可靠。若业务逻辑可能涉及负输入(比如坐标、索引偏移),直接用 % 就埋了隐患。
- 安全写法是
(n & 1) == 0(偶)或(n & 1) != 0(奇),对所有有符号整数都成立,因为补码下最低位直接反映奇偶性 -
unsigned类型下%和&行为一致,但统一用&更省心 - 现代编译器(如 GCC -O2、Clang)对
n % 2确实常优化为n & 1,但别依赖——自己写&才是明确意图
& 1 为什么比 % 2 更快?不是“位运算天生快”,而是没除法开销
CPU 执行 % 操作本质是整数除法指令(如 x86 的 idiv),延迟高、吞吐低;而 & 是单周期逻辑门操作。即使编译器优化掉了除法,手写 & 1 能避免任何潜在的符号处理分支(比如对负数先取绝对值再模)。
实测在密集循环中(如遍历百万级数组做奇偶分流),裸写 n & 1 比 n % 2 稳定快 10%–20%,尤其在未开启高级优化(-O2/-O3)时差异更明显。
立即学习“C++免费学习笔记(深入)”;
- 注意:仅对 2 的幂次模数(2, 4, 8…)才能无条件转为位与;
n % 3就不能用位运算替代 -
int、long、size_t等整型,& 1都安全;但bool或char也适用,无需零扩展担忧 - 不要写
n & 0x1——1字面量已足够清晰,多写十六进制反而干扰可读性
编译器是否真会把 % 2 优化成 & 1?看汇编最准
别猜,用 g++ -S -O2 生成汇编验证。例如:
int is_even(int n) { return n % 2 == 0; }
在主流 x86-64 编译器下,输出通常是:
testl $1, %edi je .L2
即用 test(等价于 and)测试最低位,而非调用除法。但如果函数参数是 int64_t n 且编译目标是 32 位平台,或启用了 -fwrapv 等特殊标志,优化可能失效。关键点在于:优化存在前提,而 & 1 没有任何前提。
- 检查方法:对含负数的测试用例(如
is_even(-5))跑单元测试,确保行为符合预期 - 模板函数里若
T是用户自定义类型,%可能触发重载,&则直接编译失败——此时必须显式约束为整型 - Clang 的
-Rpass=loop-vectorize可提示是否因奇偶判断阻碍向量化;& 1更易被识别为 trivial predicate
实际项目中该选哪个?看场景,但默认选 & 1
除非代码要跑在连基本位运算都不支持的嵌入式古董芯片上(现实中几乎不存在),否则 & 1 是更可靠、更高效、更少意外的选择。它不增加理解成本,和 % 2 一样直白,还绕开了 C++ 标准里关于负数取模的灰色地带。
唯一需要犹豫的是可读性争议——有人觉得 % 更“自文档化”。但团队里只要约定一条 lint 规则(如 “禁止在奇偶判断中使用 %”),配合一次代码审查,这个习惯很快就能固化。毕竟,真正的可读性来自一致性,而不是单个表达式的表面含义。
补码体系下最低位就是奇偶标志,这个事实比任何注释都硬。







