C 语言17:位操作符 & | ^:从二进制编码到大小端

在 C 语言中,位操作符是直接操控二进制世界的 "手术刀"。

其中 ^(按位异或)因独特的运算规则,在数据交换、加密、校验等场景中大放异彩。本文将从异或的核心特性出发,详解其经典用法,并结合大端 / 小端字节序知识,展示位操作在底层开发中的实际价值。

一、前置知识:正码、反码、补码(二进制编码基础)

计算机中整数的二进制编码是位操作的前提,尤其是有符号数的补码规则,直接决定了位操作的结果。

核心规则

  • 正数:正码 = 反码 = 补码(符号位为 0,数值位为二进制值);
  • 负数:补码 = 反码 + 1(反码是符号位不变、数值位取反);
  • 补码优势 :统一零的表示(只有0000 0000),减法可转化为加法(a - b = a + (-b)的补码运算)。

例如 8 位整数:

  • +5的补码:0000 0101
  • -5的补码:1111 1011(反码1111 1010加 1)。

二、按位异或(^):不同为 1,相同为 0

异或是最灵活的位操作符,运算规则为:两个二进制位相同则结果为 0,不同则为 1 。示例:1011 0010 ^ 0110 1011 = 1101 1001(逐位对比,不同为 1)。

核心特性(异或 "三定律")

  1. 自反性a ^ a = 0(相同位运算结果为 0);
  2. 恒等性a ^ 0 = a(与 0 运算结果不变);
  3. 交换律与结合律a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ ca ^ b = b ^ a

场景 1:不使用临时变量交换两个整数(高效交换)

利用异或的 "三定律",可在不借助第三方变量的情况下交换两个整数的值,核心逻辑是通过三次异或操作 "覆盖" 原有值。

原理推导

设整数ab,目标是交换二者的值(abba):

  1. 第一步:a = a ^ b(此时a存储a^bb仍为原值);
  2. 第二步:b = a ^ b(代入a的新值,得(a^b) ^ b = a ^ (b^b) = a ^ 0 = ab变为原来的a);
  3. 第三步:a = a ^ b(此时b是原来的a,得(a^b) ^ a = b ^ (a^a) = b ^ 0 = ba变为原来的b)。
代码示例(含数值演示)
cpp 复制代码
#include <stdio.h>

void xorSwap(int *a, int *b) 
{
    if (a == b) return; // 避免同地址变量清零(a^a=0)
    printf("交换前:a=%d, b=%d\n", *a, *b);
    
    *a = *a ^ *b;  // 步骤1:a = 原a ^ 原b
    *b = *a ^ *b;  // 步骤2:b = (原a^原b) ^ 原b = 原a
    *a = *a ^ *b;  // 步骤3:a = (原a^原b) ^ 原a = 原b
    
    printf("交换后:a=%d, b=%d\n", *a, *b);
}

int main() 
{
    int x = 3, y = 5;
    xorSwap(&x, &y); 
    return 0;
}
数值拆解(二进制视角)
  • a=30000 0011),原b=50000 0101);
  • 步骤 1:a = 3 ^ 5 = 0000 0110(6);
  • 步骤 2:b = 6 ^ 5 = 0000 0011(3,即原a);
  • 步骤 3:a = 6 ^ 3 = 0000 0101(5,即原b)。
优缺点分析
  • 优点:无需临时变量(节省栈空间),异或运算在硬件层实现,效率高于加减法;
  • 缺点 :可读性差(不如temp=a; a=b; b=temp直观),且ab必须指向不同地址(同地址会因a^a=0导致数据清零)。

场景 2:数据加密与解密(对称加密基础)

异或的 "可逆性" 使其成为轻量级加密的首选:用密钥key对数据data加密(data ^ key),解密时再次异或密钥即可恢复原数据((data ^ key) ^ key = data)。

示例:字符串加密
cpp 复制代码
#include <stdio.h>
#include <string.h>

// 异或加密/解密(同一函数)
void xorCrypt(char *data, const char *key) 
{
    int dataLen = strlen(data);
    int keyLen = strlen(key);
    for (int i = 0; i < dataLen; i++) 
    {
        data[i] = data[i] ^ key[i % keyLen]; // 循环使用密钥
    }
}

int main() 
{
    char text[] = "Hello, XOR Crypt!";
    char key[] = "secret";
    
    printf("原始数据:%s\n", text);
    xorCrypt(text, key);
    printf("加密后:%s(二进制乱码)\n", text); // 输出乱码
    xorCrypt(text, key);
    printf("解密后:%s\n", text); // 恢复原始数据
    return 0;
}

场景 3:检测二进制位差异(校验数据变化)

异或结果的 "1" 位表示两个数的二进制位不同,可用于检测数据是否被修改(如校验和)。例如比较两个整数的差异位:

cpp 复制代码
#include <stdio.h>

// 输出两个数的差异位(1表示不同)
void printDiffBits(int a, int b) 
{
    int diff = a ^ b; // 差异位为1

    printf("a=%d, b=%d 的差异位:", a, b);

    for (int i = 0; i < 32; i++)  // 32位整数
    { 
        if (diff & (1 << i))   // 检测第i位是否为1
        { 
            printf("%d ", i);
        }
    }
    printf("\n");
}

int main() 
{
    printDiffBits(3, 5); // 3是011,5是101,差异位为0和2 → 输出"0 2"
    return 0;
}

三、大端与小端:位操作与字节序的关联

多字节数据(如intlong)在内存中的字节排列顺序(大端 / 小端),会影响内存层面的位操作,但不改变异或等运算的逻辑结果。

1. 大端与小端的核心区别

  • 大端模式 :高位字节存于低地址(符合人类读写习惯,如0x1234在内存中为0x12(低地址)、0x34(高地址));
  • 小端模式 :低位字节存于低地址(硬件友好,如0x1234在内存中为0x34(低地址)、0x12(高地址))。

2. 用位操作检测大小端

通过&操作符读取多字节数据的第一个字节(低地址),判断是否为低位字节:

cpp 复制代码
#include <stdio.h>

// 用&操作符检测大小端
void checkEndian() 
{
    int num = 1; // 32位表示:0x00000001
    char *p = (char *)&num; // 指向低地址字节
    
    // 若低地址字节为0x01(低位字节),则为小端
    if (*p & 0x01) 
    { 
        printf("小端模式(Little-Endian)\n");
    } 
    else 
    {
        printf("大端模式(Big-Endian)\n");
    }
}

int main() 
{
    checkEndian(); // x86架构通常输出"小端模式"
    return 0;
}

3. 异或操作与大小端的关系

异或是 "按二进制位" 运算,操作的是数据的逻辑值(补码),与字节在内存中的存储顺序(大小端)无关。例如:

  • 整数0x1234在小端内存中是0x34 0x12,在大端中是0x12 0x34,但逻辑值均为00010010 00110100
  • 异或运算0x1234 ^ 0x5678的结果,无论大小端,逻辑上都是逐位计算的结果(0x444C)。

4. 大小端对跨平台位操作的影响

当需要直接操作内存中的字节(如解析文件、网络数据)时,需考虑大小端:

  • 网络协议(TCP/IP)采用大端,小端机器发送数据前需用htons(主机到网络字节序)转换;
  • 异或可用于大小端转换:例如将小端的0x34 0x12转为大端0x12 0x34,可通过(num >> 8) ^ (num << 8) ^ num(本质是交换高低字节)。

四、总结:位操作符的核心价值

  • ^(异或)凭借自反性和可逆性,在无临时变量交换、加密、差异检测中不可替代;
  • 大端与小端是内存存储的字节顺序差异,不影响位操作的逻辑结果,但在跨平台数据交互中需显式处理;
  • 位操作的高效性(直接操作硬件寄存器级别的二进制位)使其成为底层开发(嵌入式、驱动)的必备工具。

掌握位操作符,不仅能写出更高效的代码,更能让你看透数据在计算机中的本质形态 ------ 二进制的世界,逻辑即力量。

相关推荐
isyangli_blog7 小时前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
vb2008118 小时前
FastAPI APIRouter
开发语言·python
Benszen8 小时前
KVM虚拟化解决方案
开发语言·perl
会编程的土豆8 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木8 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
杨充8 小时前
1.3 浮点型数据设计灵魂
开发语言·python·算法
噜噜噜阿鲁~8 小时前
python学习笔记 | 11.3、面向对象高级编程-多重继承
java·开发语言
basketball6169 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
春生野草9 小时前
反射、Tomcat执行
java·开发语言
雪的季节10 小时前
企业级 Qt 全功能项目
开发语言·数据库·qt