一、基础运算符速查

运算符含义典型用途
&按位与掩码提取特定位
|按位或设置标志位
^按位异或切换位、简单加密
~按位取反生成全 1 掩码
<<左移快速乘 2、生成位掩码
>>右移快速除 2、提取高位

二、标志位的设置、清除与检测

用宏封装位操作,可读性更好,也便于移植到不同位宽平台。

bit_flags.c
#include <stdio.h>

#define FLAG_READ   (1u << 0)   /* 0001 */
#define FLAG_WRITE  (1u << 1)   /* 0010 */
#define FLAG_EXEC   (1u << 2)   /* 0100 */

#define SET_BIT(x, b)   ((x) |= (b))
#define CLEAR_BIT(x, b) ((x) &= ~(b))
#define TOGGLE_BIT(x,b) ((x) ^= (b))
#define TEST_BIT(x, b)  (((x) & (b)) != 0)

int main(void) {
    unsigned int perm = 0;
    SET_BIT(perm, FLAG_READ | FLAG_WRITE);
    printf("read=%d write=%d exec=%d\n",
           TEST_BIT(perm, FLAG_READ),
           TEST_BIT(perm, FLAG_WRITE),
           TEST_BIT(perm, FLAG_EXEC));
    /* 输出:read=1 write=1 exec=0 */
    return 0;
}
注意: 对 signed 类型做位运算时,右移行为(算术/逻辑)由编译器决定。位运算操作数应优先使用 unsigned 类型。

三、位域:紧凑存储多字段

位域让结构体按位分配空间,适合寄存器映射、协议包头等场景。

bitfield.c
#include <stdio.h>

typedef struct {
    unsigned int opcode : 4;   /* 4 bit */
    unsigned int reg_dst : 3;  /* 3 bit */
    unsigned int reg_src : 3;  /* 3 bit */
    unsigned int imm     : 6;  /* 6 bit */
} Instr;

int main(void) {
    Instr i = {0xA, 2, 3, 0x1F};
    printf("sizeof(Instr)=%zu\n", sizeof(i));  /* 通常 2 或 4 字节 */
    return 0;
}
陷阱: 位域的内存布局(从高位还是低位开始)由实现定义,不可跨平台直接 memcpy 到硬件寄存器。需要时用显式掩码替代。

四、大小端判断与字节交换

网络协议用大端(Big-Endian),x86 用小端(Little-Endian)。编写跨平台代码时必须处理。

endian.c
#include <stdint.h>
#include <stdio.h>

/* 联合判断大小端 */
int is_little_endian(void) {
    union { uint32_t i; uint8_t c[4]; } u = {1};
    return u.c[0] == 1;
}

/* 32 位字节交换 */
uint32_t bswap32(uint32_t x) {
    return ((x & 0xFF000000) >> 24) |
           ((x & 0x00FF0000) >>  8) |
           ((x & 0x0000FF00) <<  8) |
           ((x & 0x000000FF) << 24);
}

/* GCC/Clang 内置版本(更高效) */
/* #define bswap32(x) __builtin_bswap32(x) */

int main(void) {
    printf("little endian: %s\n", is_little_endian() ? "yes" : "no");
    printf("0x12345678 -> 0x%08X\n", bswap32(0x12345678));
    return 0;
}

五、实用技巧:判断 2 的幂、取最低位 1、汉明重量

bit_tricks.c
#include <stdint.h>
#include <stdio.h>

/* 判断是否为 2 的幂:二进制只有一个 1 */
int is_power_of_2(uint32_t x) {
    return x && !(x & (x - 1));
}

/* 提取最低位的 1 所代表的值 */
uint32_t lowbit(uint32_t x) {
    return x & -x;
}

/*  Brian Kernighan 算法:统计 1 的个数 */
int popcount(uint32_t x) {
    int c = 0;
    for (; x; c++) x &= x - 1;
    return c;
}

int main(void) {
    printf("is_power_of_2(16)=%d\n", is_power_of_2(16));
    printf("lowbit(12)=%u\n", lowbit(12));      /* 1100 -> 0100 = 4 */
    printf("popcount(0b1011)=%d\n", popcount(0b1011));
    return 0;
}

六、总结

位运算的价值在于「用最低成本操控数据」。在资源受限的嵌入式环境、高性能网络协议解析、加密算法实现中,合理使用位运算能显著减少内存占用与计算开销。建议把常用操作封装成宏或内联函数,并始终使用 unsigned 类型避免符号扩展带来的意外。