上节回顾:上一讲深入分析了多平台大小端(Endianess)问题,包括其原理、典型陷阱(如结构体直接读写、网络通信字节序混乱等)以及如何通过显式字节序转换、逐字段序列化等方法规避相关风险。
1. 主题原理与细节逐步讲解
1.1 异或(XOR)运算基础
-
异或运算符为
^,对位操作,规则是:相同为0,不同为1。 -
真值表如下:
A B A ^ B 0 0 0 0 1 1 1 0 1 1 1 0 -
常用性质:
- 任何数与自身异或结果为0:
a ^ a == 0 - 任何数与0异或结果为自身:
a ^ 0 == a - 异或满足交换律、结合律:
a ^ b == b ^ a,(a ^ b) ^ c == a ^ (b ^ c)
- 任何数与自身异或结果为0:
1.2 常见异或技巧
- 数据加密/解密:
data ^ key,再次异或相同key即可还原(对称加密的最基础形式)。 - 无临时变量交换两个整数:
a = a ^ b; b = a ^ b; // b = (a ^ b) ^ b = a a = a ^ b; // a = (a ^ b) ^ a = b - 查找数组中唯一的单个元素(其它元素均成对出现):
int res = 0; for (int i = 0; i < n; ++i) { res ^= arr[i]; } // res即为那个单独出现的数 - 校验/冗余校验码(如XOR校验)
2. 典型陷阱/缺陷说明及成因剖析
2.1 异或交换变量的误用与副作用
- a和b指向同一内存时,用异或法交换会变为0:
int a = 5; int *p = &a; *p = *p ^ *p; // 结果为0,而不是a本身 - 可读性差:异或法交换不如临时变量法直观,易出错,不推荐用于实际工程。
2.2 数据类型和溢出问题
- 异或操作是逐位的,若数据类型不一致(如int与char),可能引入隐式类型转换,导致意外行为。
- 溢出不会报错,低位截断,易隐藏bug。
2.3 用于加解密的安全风险
- 异或加密极易被破解,对称性太强,不能用于任何安全场合。
- 若key过短或可预测,安全性极低。
2.4 异或校验的局限性
- 只适合检测单个位翻转,不能检测偶数个位错误(例如两个位同时出错,异或后相互抵消)。
3. 规避方法与最佳设计实践
3.1 变量交换优先用临时变量
- 推荐:
int tmp = a; a = b; b = tmp; - 清晰、安全,防止同一变量/地址引发灾难。
3.2 数据类型一致
- 异或操作前,确保参与变量类型一致,或显式强制类型转换。
3.3 密码学场合禁止用异或
- 切勿用异或做真正加密,除非你能100%确保密钥不可预测且与消息等长(即一次性密码本,否则不可用)。
3.4 校验码需求高时选更强算法
- 对数据完整性有更高要求时,使用CRC、MD5、SHA等更复杂的校验或哈希算法。
4. 典型错误代码与优化后正确代码对比
错误示例1:异或交换同一变量
int a = 5;
a = a ^ a;
a = a ^ a;
a = a ^ a;
// a最终为0,非原始值
分析:a与自身异或始终为0,无法还原原值,逻辑错误。
正确示例1:标准安全交换
int a = 5, b = 10;
int tmp = a;
a = b;
b = tmp;
或
if (&a != &b) {
a ^= b;
b ^= a;
a ^= b;
}
注意:加指针判断,防止同一变量地址。
错误示例2:异或校验易掩盖多位错误
unsigned char arr[4] = {0xAB, 0xCD, 0xAB, 0xCD};
unsigned char xor_sum = 0;
for (int i = 0; i < 4; i++)
xor_sum ^= arr[i];
// 若arr[0]和arr[2]同时出错,xor_sum结果一样,无法发现
正确示例2:用CRC等更强校验
// 采用CRC-8/16/32等标准算法,能发现更多类型的错误
// 示例:简单CRC-8实现(多项式:0x07)
unsigned char crc8(unsigned char *data, int len) {
unsigned char crc = 0x00;
for (int i = 0; i < len; i++) {
crc ^= data[i];
for (int j = 0; j < 8; j++) {
if (crc & 0x80) {
crc = (crc << 1) ^ 0x07; // 多项式0x07
} else {
crc <<= 1;
}
}
}
return crc;
}
// 使用示例
unsigned char arr[4] = {0xAB, 0xCD, 0xAB, 0xCD};
unsigned char checksum = crc8(arr, 4);
// 校验时重新计算并比较,能检测多位错误
解释:CRC(循环冗余校验)通过多项式除法,能检测出更多的错误类型,包括偶数位翻转、多位错误等,比异或校验更可靠。实际工程中建议使用标准库或成熟的CRC实现(如zlib中的crc32)。
5. 底层原理补充说明
- 异或操作可直接映射为单条CPU指令,效率极高。
- 交换变量时,若为同一内存地址,连续异或会导致值归零,不可恢复。
- 在数组查找唯一数等场合,异或结合交换律和结合律,能高效消除偶数次出现的元素,仅剩下单数次的元素。
6. 异或交换变量原理

7. 总结与实际建议
- 异或运算在位操作、唯一数查找、简易校验中非常高效,但其交换变量方法有严重副作用,不推荐实际工程中使用。
- 类型不一致、同地址操作、数据校验局限等都是常见陷阱。
- 对安全性有要求时,绝不要用异或做加密。
- 工程实践中,优先追求代码可读性和健壮性,慎用异或技巧。
异或是C语言位运算中的利器,合理使用能简化部分场景,但必须警惕其隐藏的类型、地址、可读性等风险,务必结合工程需求权衡使用。
如需了解CRC实现、位运算优化或其它底层高效算法,欢迎继续提问!
公众号 | FunIO
微信搜一搜 “funio”,发现更多精彩内容。
个人博客 | blog.boringhex.top
940

被折叠的 条评论
为什么被折叠?



