news 2026/7/3 8:45:33

C语言:位运算实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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。

核心特性

  • 任何数和自身异或结果为 0:x ^ 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 变 1,1 变 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(低字节)
0x10010x340x56
0x10020x560x34
0x10030x78(低字节)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:什么是大端小端?有哪些方法可以判断机器的字节序?

答: 大端是高字节存在低地址,低字节存在高地址;小端是低字节存在低地址,高字节存在高地址。 判断方法主要有两种:

  1. 联合体法:利用联合体共享内存的特性,给 int 赋值 1,读取 char 成员的值判断
  2. 指针法:将 int 指针强转为 char 指针,读取第一个字节判断 其中联合体法代码更简洁,是面试首选写法。

Q2:n & (n-1)有什么作用?能解决哪些经典问题?

答: 作用是消去 n 二进制中最右边的一个 1。 可以解决的经典问题:

  1. 统计二进制中 1 的个数
  2. 判断一个数是不是 2 的整数次幂
  3. 判断一个数是不是 4 的幂的衍生题
  4. 消除二进制末尾连续的 0

Q3:左移和右移对于有符号数和无符号数有什么区别?

答:

  • 左移:两者都是低位补 0,高位丢弃。但有符号数左移如果改变了符号位,属于未定义行为。
  • 右移:无符号数是逻辑右移,高位补 0;有符号数是算术右移,高位补符号位,正数补 0,负数补 1。 工程中位操作推荐使用无符号类型,避免符号位带来的未定义行为。

Q4:位运算相比算术运算有什么优势?适用哪些场景?

答: 优势:位运算直接操作二进制,CPU 指令周期更短,执行效率更高,且不需要额外的硬件乘法器等资源。 适用场景:

  1. 嵌入式硬件寄存器操作
  2. 网络协议字段解析
  3. 标志位管理,节省内存
  4. 算法优化、加密校验算法
  5. 资源受限的单片机开发

Q5:异或运算有哪些核心特性?有哪些典型应用?

答: 核心特性:自身异或为 0,和 0 异或为自身,满足交换律结合律。 典型应用:

  1. 不用临时变量交换两个数
  2. 找出数组中只出现一次的数字
  3. 简单加密解密
  4. 数据校验
  5. 翻转指定比特位

2. 常见易错坑点

  1. 位操作使用有符号数,右移、左移出现符号位问题,引发未定义行为
  2. 移位运算符优先级低于加减,忘记加括号导致运算结果错误
  3. 移位位数超过数据类型的位数,属于 C 语言未定义行为,结果不可预期
  4. 寄存器位操作时,清位忘记取反,直接和 0 相与导致所有位都被清零
  5. 大小端处理时,混淆高低字节顺序,跨平台通信数据解析错误
  6. 位掩码使用有符号 1 左移,高位溢出后变成负数,引发后续逻辑错误
  7. 误以为位运算万能,过度使用导致代码可读性极差,维护成本飙升

以上就是 C 语言位运算的全部实战核心内容,从工程封装到笔试算法全覆盖,是嵌入式、底层开发必须熟练掌握的基础技能。


制作不易,如果对你有用,希望能点赞收藏支持一下。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/3 8:41:39

EasyMarkets综合评析:品牌规范性如何影响用户体验

EasyMarkets综合评析&#xff1a;品牌规范性如何影响用户体验评估外汇服务平台时&#xff0c;真正有价值的不是热闹的宣传语&#xff0c;而是基础流程是否清楚、风险提示是否及时、服务沟通是否稳定。EasyMarkets作为受到关注的品牌&#xff0c;适合放在更细致的评测框架中观察…

作者头像 李华
网站建设 2026/6/29 0:27:01

AI Agent 学习路线图:新手小白必收藏,照着做就能上手大模型

本文提供了一份详尽的 AI Agent 学习路线图&#xff0c;从基础概念到实际应用&#xff0c;涵盖 Agent Loop、工具调用、RAG、Memory 等核心内容&#xff0c;并推荐了 Claude Code、OpenClaw、Hermes 等现代 Agent Harness 项目。适合新手和有经验的开发者&#xff0c;旨在帮助读…

作者头像 李华
网站建设 2026/6/29 0:27:02

网盘下载速度革命:九大平台直链解析工具深度体验

网盘下载速度革命&#xff1a;九大平台直链解析工具深度体验 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 &#xff0c;支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘 …

作者头像 李华
网站建设 2026/6/29 0:27:01

2026年程序员涨薪指南:收藏这5个高薪方向,小白也能抓住AI红利!

本文揭示了2026年程序员涨薪的关键方向&#xff0c;包括工程化AI、云原生国产化、高级全栈、数据安全与合规&#xff0c;以及AI Agent开发。文章强调&#xff0c;仅掌握基础技能已不足够&#xff0c;需深入理解各领域技术细节与业务价值&#xff0c;才能在AI时代获得更好的职业…

作者头像 李华
网站建设 2026/6/29 0:27:03

IDEA数据库管理避坑清单(含23个真实生产事故复盘)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;IDEA数据库管理避坑清单&#xff08;含23个真实生产事故复盘&#xff09; IntelliJ IDEA 内置的 Database Tools 是开发者高频使用的轻量级数据库管理模块&#xff0c;但其隐式行为与配置陷阱极易引发数据误删…

作者头像 李华
网站建设 2026/6/29 0:27:08

stm32L单片机ETHERNET以太网web页面控制led实验

已经实际验证过了&#xff0c;具体看代码&#xff0c;免费下载&#xff0c;欢迎交流学习。分享的文件&#xff1a;10.STM32....rar 链接&#xff1a;https://pan.baidu.com/s/1gIJF_R8HtVRHdnBXyTJa1w?pwdA8Xu

作者头像 李华