异或交换仅适用于地址不同、平凡可复制的整型变量,需防护自交换和类型不匹配;std::swap更安全通用,现代编译器下性能相当且支持用户类型与adl。

用 ^ 交换两个整型变量,前提是不能用临时变量
异或交换法成立的前提是:两个变量地址不同、且都是整型(或可按位操作的标量类型)。它利用 a ^ a == 0 和 a ^ 0 == a 这两条性质推导出:a = a ^ b; b = a ^ b; a = a ^ b; 三步后完成交换。
常见错误现象:a 和 b 是同一个变量(比如传入 swap(x, x)),此时第一步就把值变没了,最后得到全 0。C++ 标准不保证这种自交换行为,实际结果依赖编译器优化和寄存器分配,多数情况下出错。
- 只适用于
int、short、char等整型,浮点数、指针、类对象不能直接用^ - 如果变量是
volatile或位于内存映射 I/O 地址,多次读写可能触发副作用,不能用 - 现代编译器对
std::swap优化极好,异或写法反而可能阻碍优化(比如破坏寄存器复用)
std::swap 和异或写法在性能与安全上的实际差异
在开启 O2 优化的 Clang/GCC 下,std::swap 对内置类型通常被内联为 3 条 xor 汇编指令,和手写异或完全一致;但对用户类型(如 std::vector),它会走移动语义,而异或根本无法编译通过。
容易踩的坑:有人把异或写成宏或模板试图“通用化”,比如 #define SWAP(a,b) a^=b,b^=a,a^=b,但若 a 是表达式(如 *p++),宏会展开成多次求值,导致指针错位或未定义行为。
立即学习“C++免费学习笔记(深入)”;
- 异或交换没有类型检查,
char和int混用会静默截断 -
std::swap支持 ADL(参数依赖查找),能自动调用用户为自定义类型重载的swap - 在调试模式下,异或写法单步跟踪时逻辑难读,中间态全是“无意义”的异或结果
哪些场景真该用手写异或?几乎不存在
嵌入式裸机开发中,若栈空间极度紧张、又确定只交换两个 uint8_t 变量,且编译器连 std::swap 都没启用(比如用 C++98 + 手写头文件),才可能考虑。但更现实的做法是关掉异常/RTTI,让 std::swap 编译出来就是三条 xor。
典型误用:在面试题里硬套异或交换,却忽略 std::swap 对 std::array、std::pair 的完美转发支持——这些类型用异或根本没法写。
- 异或交换无法处理引用、const 限定符、bit-field 成员
- Clang 的
-Wxor-used-as-pow警告虽不针对此,但说明编译器对^的语义敏感度远高于开发者直觉 - LLVM IR 层面,
std::swap更易被识别为“交换”意图,利于后续向量化或寄存器分配
真要写异或交换,必须加的防护条件
如果因历史代码约束或教学演示必须保留异或写法,至少得确保不会拿同一个地址操作。可用 std::addressof 做运行时检查(仅调试版):
if (std::addressof(a) == std::addressof(b)) return; // 防自交换
但这只是补救,不是替代方案。真正关键的是:确认类型是平凡可复制(std::is_trivially_copyable_v<t></t>),且不涉及对齐敏感访问(比如 __attribute__((packed)) 结构体成员)。
- 不要对
bool用异或交换——它可能被编译器打包成 bit,^操作未定义 - 避免在多线程环境下对同一变量做无锁异或交换,缺少内存序约束
- 即使满足所有条件,也应在注释里写明:“仅限 POD 整型,已验证地址不等”
异或交换的边界比看起来窄得多,稍一越界就是未定义行为,而 std::swap 的边界由标准明确定义。事情说清了就结束。








