1. 位运算基础

各位同学,欢迎来到《位运算技巧与协议解析实战》的第一章。

说实话,我做了十几年嵌入式开发,跟各种协议、寄存器打了无数交道。回头一看,最基础的东西往往最容易被忽视。位运算就是这样的存在——你越懂它,写出来的代码就越优雅、越高效。

1.1 二进制:计算机的母语

计算机只认0和1。这不是什么新鲜事,但你真的理解二进制吗?

我见过不少工程师,写代码时习惯用十进制思考。比如设置一个寄存器,直接写 0x3F 就完事了。但遇到需要只改某一位的情况,就傻眼了。

二进制数,说白了就是逢二进一。每一位的权重是2的幂次方:

二进制: 1 0 1 1
权重:   8 4 2 1
计算:   8 + 0 + 2 + 1 = 11 (十进制)

核心要点:二进制每一位只有0或1,对应电路里的「开」和「关」。这就是为什么位运算如此高效——它直接操作硬件逻辑。

1.2 十六进制:工程师的速记法

二进制写起来太长了。一个32位的数,写出来32个0和1,谁受得了?

十六进制就是为此而生。4位二进制对应1位十六进制,简洁又直观。

二进制 十六进制 十进制
0000 0 0
0001 1 1
1010 A 10
1111 F 15

我个人习惯,在代码里看到 0xAB,脑子里立刻反应出 1010 1011。这需要练习,但一旦熟练了,读协议文档就像看母语一样快。

小技巧:调试时把变量打印成十六进制,比十进制更容易看出位模式。我在调试I2C协议时,经常用 printf("0x%02X", data) 来观察数据。

1.3 原码、反码、补码:负数的三种面孔

为什么计算机要用补码?这个问题我当年也困惑过。

先看原码:最高位表示符号,0正1负。比如 0000 0011 是3,1000 0011 是-3。

反码:正数不变,负数除了符号位外全部取反。

补码:正数不变,负数在反码基础上加1。

举个例子,用8位表示:

数字: +3
原码: 0000 0011
反码: 0000 0011
补码: 0000 0011

数字: -3
原码: 1000 0011
反码: 1111 1100
补码: 1111 1101

为什么会这样?你想想看,如果直接用原码做加法,3 + (-3) 会得到 0000 0110,也就是6,这显然不对。

补码的好处是:加法和减法可以用同一套电路。 3 + (-3) 用补码算:0000 0011 + 1111 1101 = 1 0000 0000,溢出后正好是0。

注意:我曾经在写一个传感器驱动时,因为没注意补码转换,读出来的温度值总是差几度。查了半天才发现是负数解析错了。嗯,这里要特别小心——有符号数的右移操作,不同编译器行为可能不一样。

1.4 与、或、非、异或:位运算四剑客

这四个运算,是位操作的基本功。我几乎每天都在用。

按位与(&)

两个位都是1,结果才是1。常用于清零或提取特定位。

  1010 1101
& 0000 1111
-----------
  0000 1101  // 只保留低4位

我在项目中遇到过:需要读取一个寄存器的低3位,其他位忽略。一行 value & 0x07 搞定。

按位或(|)

只要有一个是1,结果就是1。常用于置位。

  1010 1101
| 0000 0011
-----------
  1010 1111  // 低2位置1

按位非(~)

0变1,1变0。注意这是单目运算符。

~ 1010 1101
-----------
  0101 0010

我建议你小心使用 ~,尤其是在有符号数上。比如 ~0 在32位系统上是 0xFFFFFFFF,也就是-1。

按位异或(^)

相同为0,不同为1。这个运算特别有意思。

  1010 1101
^ 0000 1111
-----------
  1010 0010  // 低4位取反

异或有个经典用法:交换两个变量,不用临时变量。

a = a ^ b;
b = a ^ b;
a = a ^ b;

不过说实话,现在编译器优化得很好,这种写法更多是为了炫技。我一般只在加密算法里大量使用异或。

实战口诀:

  • 想清零某几位?用 & 配合掩码
  • 想置位某几位?用 | 配合掩码
  • 想翻转某几位?用 ^ 配合掩码
  • 想取反所有位?用 ~

知识体系总览

下面这张图,是我梳理的本章知识结构。你可以把它当作一个思维导图来看:

位运算基础 二进制 逢二进一 权重:2^n 十六进制 4位→1位 0x前缀 原码/反码/补码 负数表示法 补码:加法统一 与或非异或 & 清零/提取 | 置位 ~ 取反 ^ 翻转/交换 核心思想:位运算 = 直接操作硬件逻辑 高效、简洁、可预测

这张图把本章的知识点串起来了。你从中心往外看,二进制是基础,十六进制是工具,原反补是理解负数的关键,而四个基本运算就是你的武器。

我的建议:刚开始学的时候,拿一张纸,自己写几个二进制数,手动算一遍与或非异或。别嫌麻烦。我当年就是这么练的,后来看协议文档里的位域定义,一眼就能看出该怎么操作。

好了,这一章就到这里。记住:位运算不是冷冰冰的数学,它是你和硬件对话的语言。掌握了它,你写驱动、调协议、优化性能,都会顺手很多。

可可精品资料库