一、基础运算符速查
| 运算符 | 含义 | 典型用途 |
|---|---|---|
| & | 按位与 | 掩码提取特定位 |
| | | 按位或 | 设置标志位 |
| ^ | 按位异或 | 切换位、简单加密 |
| ~ | 按位取反 | 生成全 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 类型避免符号扩展带来的意外。