C语言:位运算实战 前言位运算是嵌入式底层、硬件驱动、网络协议、笔试算法的核心刚需能力直接操作二进制比特位执行效率远高于普通算术运算是底层开发者的必备技能。本篇从基础运算符、工程常用技巧到硬件寄存器操作、大小端处理与笔试高频手撕题全覆盖全部为工业界真实落地用法兼顾面试考点与工程实战价值。一、六大基础位运算符核心用法1. 按位与 运算规则对应二进制位同为 1结果才为 1否则为 0。工程核心用途清零指定比特位和 0 相与即可清零保留指定比特位和 1 相与即可保留判断奇偶x 1结果为 1 则是奇数0 则是偶数比取模运算效率更高// 判断奇偶 int is_odd(int x) { return x 1; } // 保留低8位其余位清零 uint32_t keep_low8(uint32_t val) { return val 0xFF; }2. 按位或 |运算规则对应二进制位有一个为 1结果就为 1。工程核心用途置 1 指定比特位和 1 相或即可置 1合并多个标志位多个状态位按位或组合成一个状态字// 将第3位从0开始计数置1不影响其他位 uint32_t set_bit3(uint32_t val) { return val | (1 3); }3. 按位异或 ^运算规则对应二进制位不同为 1相同为 0。核心特性任何数和自身异或结果为 0x ^ x 0任何数和 0 异或结果为自身x ^ 0 x满足交换律和结合律工程核心用途翻转指定比特位和 1 异或翻转和 0 异或不变交换两个变量无需临时变量加密、校验算法基础// 不用临时变量交换两个整数 void swap(int *a, int *b) { *a *a ^ *b; *b *a ^ *b; *a *a ^ *b; }4. 按位取反运算规则所有二进制位 0 变 11 变 0。工程核心用途配合与运算实现指定位置零写法更简洁。// 将第3位清零其他位不变 uint32_t clear_bit3(uint32_t val) { return val ~(1 3); }注意~是单目运算符对整个数据类型所有位取反操作有符号数时需注意符号位变化。5. 左移 运算规则二进制位整体左移 n 位高位丢弃低位补 0。数学意义无符号数左移 n 位等价于乘以 2 的 n 次方效率远高于乘法。工程用途生成位掩码、数值快速乘 2。6. 右移 运算规则二进制位整体右移 n 位。无符号数逻辑右移高位补 0有符号数算术右移高位补符号位正数补 0负数补 1数学意义正数右移 n 位等价于除以 2 的 n 次方向下取整。工程用途快速除 2、提取高位字段。二、位掩码工程封装技巧位掩码是硬件驱动、协议开发的最常用手法通过掩码提取、修改寄存器中的特定字段是嵌入式开发的基础编码能力。1. 位操作通用宏封装工业界标准写法通过宏封装通用的位操作代码可读性强、不易出错// 置位将val的第n位从0开始置1 #define SET_BIT(val, n) ((val) | (1U (n))) // 清位将val的第n位清零 #define CLEAR_BIT(val, n) ((val) ~(1U (n))) // 翻转位将val的第n位取反 #define TOGGLE_BIT(val, n) ((val) ^ (1U (n))) // 读位读取val的第n位的值返回0或1 #define READ_BIT(val, n) (((val) (n)) 1U)规范细节使用1U无符号整数避免有符号数左移溢出的未定义行为。2. 多段位操作寄存器字段硬件寄存器通常按字段划分一个寄存器包含多个功能位段需要提取、修改指定段的值。// 提取位段从val中提取从offset位开始、长度为len的字段值 #define GET_FIELD(val, offset, len) \ (((val) (offset)) ((1U (len)) - 1U)) // 设置位段将val的offset位开始的len位设置为field_val #define SET_FIELD(val, offset, len, field_val) \ do { \ (val) ~(((1U (len)) - 1U) (offset)); \ (val) | ((field_val) ((1U (len)) - 1U)) (offset); \ } while(0)使用示例// 假设寄存器bit3~bit6是4位的波特率配置字段 uint32_t reg 0; SET_FIELD(reg, 3, 4, 0x0A); // 设置波特率字段为10 uint32_t baud GET_FIELD(reg, 3, 4); // 读取波特率字段值3. 标志位组合技巧系统中多个布尔状态可以合并到一个整型变量中大幅节省内存是嵌入式资源受限场景的常用手法。// 定义状态标志位 #define FLAG_POWER_ON (1 0) #define FLAG_TX_READY (1 1) #define FLAG_RX_DONE (1 2) #define FLAG_ERROR (1 3) uint8_t system_status 0; // 置位多个标志 system_status | FLAG_POWER_ON | FLAG_TX_READY; // 判断是否同时满足多个条件 if ((system_status (FLAG_POWER_ON | FLAG_TX_READY)) (FLAG_POWER_ON | FLAG_TX_READY)) { // 上电完成且发送就绪执行发送 } // 清除标志 system_status ~FLAG_ERROR;三、大小端原理与处理方案大小端是数据存储的字节序问题是跨平台通信、网络协议、嵌入式开发的必考点也是笔试高频面试题。1. 核心概念大端模式Big Endian数据的高字节存放在低地址低字节存放在高地址符合人类阅读习惯。网络字节序默认是大端。小端模式Little Endian数据的低字节存放在低地址高字节存放在高地址。x86、ARM 默认都是小端模式。以0x12345678存放在地址 0x1000 为例地址大端模式小端模式0x10000x12高字节0x78低字节0x10010x340x560x10020x560x340x10030x78低字节0x12高字节2. 手撕题判断机器大小端方法一联合体法最常用面试首选利用联合体所有成员共享同一块内存的特性通过赋值后读取第一个字节判断。int is_little_endian(void) { union { int a; char b; } u; u.a 1; return u.b; // 小端返回1大端返回0 }方法二指针法通过强制类型转换读取整型第一个字节判断。int is_little_endian(void) { int a 1; char *p (char *)a; return *p; // 第一个字节是1则为小端 }3. 大小端转换常用宏网络通信、跨平台数据交互时需要进行主机字节序和网络字节序的转换标准库提供了相关函数底层本质是移位与位运算。// 16位数据字节序翻转 uint16_t swap16(uint16_t val) { return (val 8) | (val 8); } // 32位数据字节序翻转 uint32_t swap32(uint32_t val) { return ((val 0xFF000000) 24) | ((val 0x00FF0000) 8) | ((val 0x0000FF00) 8) | ((val 0x000000FF) 24); }四、笔试高频位运算算法题1. 二进制中 1 的个数剑指 Offer 原题题目输入一个整数输出该数二进制表示中 1 的个数。最优解法消去最低位的 1核心技巧n (n-1)会消去 n 二进制中最右边的一个 1有多少个 1 就执行多少次。int hammingWeight(uint32_t n) { int count 0; while (n ! 0) { n n - 1; // 消去最右边的1 count; } return count; }优势时间复杂度 O (k)k 是 1 的个数比逐位判断 O (n) 效率更高是面试标准最优解。2. 判断一个数是不是 2 的整数次幂题目判断一个正整数是不是 2 的幂。思路2 的幂的二进制有且仅有一个 1用消去最低位 1 的技巧消一次后变为 0。bool isPowerOfTwo(int n) { if (n 0) return false; return (n (n - 1)) 0; }3. 不用加减乘除做加法题目写一个函数求两个整数之和要求不能使用 、-、*、/ 四则运算符号。思路用异或算无进位和用与运算左移算进位循环直到进位为 0。int add(int a, int b) { while (b ! 0) { int carry (unsigned int)(a b) 1; // 进位 a a ^ b; // 无进位和 b carry; } return a; }4. 找出数组中只出现一次的数字题目数组中只有一个数字出现一次其他都出现两次找出这个数字。思路利用异或特性相同数字异或为 0全部异或后剩下的就是只出现一次的数。int singleNumber(int* nums, int numsSize) { int res 0; for (int i 0; i numsSize; i) { res ^ nums[i]; } return res; }五、面试高频考点与易错坑点1. 经典面试问答Q1什么是大端小端有哪些方法可以判断机器的字节序答 大端是高字节存在低地址低字节存在高地址小端是低字节存在低地址高字节存在高地址。 判断方法主要有两种联合体法利用联合体共享内存的特性给 int 赋值 1读取 char 成员的值判断指针法将 int 指针强转为 char 指针读取第一个字节判断 其中联合体法代码更简洁是面试首选写法。Q2n (n-1)有什么作用能解决哪些经典问题答 作用是消去 n 二进制中最右边的一个 1。 可以解决的经典问题统计二进制中 1 的个数判断一个数是不是 2 的整数次幂判断一个数是不是 4 的幂的衍生题消除二进制末尾连续的 0Q3左移和右移对于有符号数和无符号数有什么区别答左移两者都是低位补 0高位丢弃。但有符号数左移如果改变了符号位属于未定义行为。右移无符号数是逻辑右移高位补 0有符号数是算术右移高位补符号位正数补 0负数补 1。 工程中位操作推荐使用无符号类型避免符号位带来的未定义行为。Q4位运算相比算术运算有什么优势适用哪些场景答 优势位运算直接操作二进制CPU 指令周期更短执行效率更高且不需要额外的硬件乘法器等资源。 适用场景嵌入式硬件寄存器操作网络协议字段解析标志位管理节省内存算法优化、加密校验算法资源受限的单片机开发Q5异或运算有哪些核心特性有哪些典型应用答 核心特性自身异或为 0和 0 异或为自身满足交换律结合律。 典型应用不用临时变量交换两个数找出数组中只出现一次的数字简单加密解密数据校验翻转指定比特位2. 常见易错坑点位操作使用有符号数右移、左移出现符号位问题引发未定义行为移位运算符优先级低于加减忘记加括号导致运算结果错误移位位数超过数据类型的位数属于 C 语言未定义行为结果不可预期寄存器位操作时清位忘记取反直接和 0 相与导致所有位都被清零大小端处理时混淆高低字节顺序跨平台通信数据解析错误位掩码使用有符号 1 左移高位溢出后变成负数引发后续逻辑错误误以为位运算万能过度使用导致代码可读性极差维护成本飙升以上就是 C 语言位运算的全部实战核心内容从工程封装到笔试算法全覆盖是嵌入式、底层开发必须熟练掌握的基础技能。制作不易如果对你有用希望能点赞收藏支持一下。