B-深入理解计算机系统第3版之第2章:信息的表示和处理

《深入理解计算机系统(原书第3版)》第2章:信息的表示和处理 📚

!note 阅读范围

本笔记依据本地附件《深入理解计算机系统(原书第3版)》的第2章(PDF 第57---176页)整理,用自己的话讲解。书中涉及的具体数值、公式和推导均来自原书材料,未在材料中确认的细节标注为"待核对"。
!tip 一句话抓住本章

计算机里的一切------数字、文字、代码------归根结底都是"0 和 1 的排列"。本章教你三种最重要的排列规则:无符号整数 (只能表示非负数)、补码 (能表示负数,C 语言的 int 就是它)、IEEE 754 浮点数 (近似表示实数,C 语言的 floatdouble 就是它),以及当你用有限位数做算术时会发生什么诡异后果(比如正数加正数居然变成负数)。

1 本章学习目标 🎯

学完本章应该掌握:

  • 十六进制、二进制、十进制的任意转换,这是阅读内存 dump、汇编输出的基本功。
  • 大端法和小端法:同一个数字在不同机器上,字节排列顺序可能完全相反。
  • 补码 :C 语言中 int 的实际存储方式,为什么 int 的负数范围比正数多 1。
  • 有符号数和无符号数混用的陷阱-1 < 0U 居然是假?这是无数 bug 的根源。
  • 整数运算溢出的后果:正数加正数变成负数、乘法导致安全漏洞。
  • IEEE 754 浮点数floatdouble 的内部构造(符号位 + 阶码 + 尾数),为什么 0.1 + 0.2 != 0.3
  • 浮点运算不可结合(a + b) + c 不等于 a + (b + c)

为什么要学这一章?没有它,你看不懂内存中存储的数据、说不清一个 C 语言程序的比较运算为什么"反直觉"、也无法理解 XDR 安全漏洞(整数溢出导致只分配了 4096 字节而不是 4GB)或是爱国者导弹拦截失败(浮点精度丢失导致 28 名士兵死亡)。

它与后面的关系:第 3 章讲机器级编程,你需要能读懂寄存器里的十六进制数、理解补码的位操作指令;第 5 章讲优化,你需要知道编译器如何用移位替代乘除法;第 9 章讲虚拟内存,你需要知道每个字节都有一个虚拟地址。

!tip 建议学习路线

  1. 先理解"位"和"字节"是什么,学会十六进制 ↔ 二进制 ↔ 十进制的互转。
  2. 掌握大端/小端,知道同一个数字在不同机器上的存储差异。
  3. 深入理解无符号数和补码的编码公式,从位模式推算出数值。
  4. 理解有符号和无符号混用时的隐式转换规则(C 语言的"潜规则")。
  5. 掌握整数加减乘除溢出时的行为及其检测方法。
  6. 最后攻克浮点数:IEEE 754 的三段式编码,为什么浮点运算不精确。

2 整章知识地图 🗺️

#mermaid-svg-O6zgy4U5hdqu7L6g{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-O6zgy4U5hdqu7L6g .error-icon{fill:#552222;}#mermaid-svg-O6zgy4U5hdqu7L6g .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-O6zgy4U5hdqu7L6g .marker{fill:#333333;stroke:#333333;}#mermaid-svg-O6zgy4U5hdqu7L6g .marker.cross{stroke:#333333;}#mermaid-svg-O6zgy4U5hdqu7L6g svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-O6zgy4U5hdqu7L6g p{margin:0;}#mermaid-svg-O6zgy4U5hdqu7L6g .edge{stroke-width:3;}#mermaid-svg-O6zgy4U5hdqu7L6g .section--1 rect,#mermaid-svg-O6zgy4U5hdqu7L6g .section--1 path,#mermaid-svg-O6zgy4U5hdqu7L6g .section--1 circle,#mermaid-svg-O6zgy4U5hdqu7L6g .section--1 polygon,#mermaid-svg-O6zgy4U5hdqu7L6g .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .section--1 text{fill:#ffffff;}#mermaid-svg-O6zgy4U5hdqu7L6g .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-depth--1{stroke-width:17;}#mermaid-svg-O6zgy4U5hdqu7L6g .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled circle,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:lightgray;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:#efefef;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-0 rect,#mermaid-svg-O6zgy4U5hdqu7L6g .section-0 path,#mermaid-svg-O6zgy4U5hdqu7L6g .section-0 circle,#mermaid-svg-O6zgy4U5hdqu7L6g .section-0 polygon,#mermaid-svg-O6zgy4U5hdqu7L6g .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-O6zgy4U5hdqu7L6g .section-0 text{fill:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .node-icon-0{font-size:40px;color:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-depth-0{stroke-width:14;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled circle,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:lightgray;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:#efefef;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-1 rect,#mermaid-svg-O6zgy4U5hdqu7L6g .section-1 path,#mermaid-svg-O6zgy4U5hdqu7L6g .section-1 circle,#mermaid-svg-O6zgy4U5hdqu7L6g .section-1 polygon,#mermaid-svg-O6zgy4U5hdqu7L6g .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .section-1 text{fill:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .node-icon-1{font-size:40px;color:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-depth-1{stroke-width:11;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled circle,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:lightgray;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:#efefef;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-2 rect,#mermaid-svg-O6zgy4U5hdqu7L6g .section-2 path,#mermaid-svg-O6zgy4U5hdqu7L6g .section-2 circle,#mermaid-svg-O6zgy4U5hdqu7L6g .section-2 polygon,#mermaid-svg-O6zgy4U5hdqu7L6g .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .section-2 text{fill:#ffffff;}#mermaid-svg-O6zgy4U5hdqu7L6g .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-depth-2{stroke-width:8;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled circle,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:lightgray;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:#efefef;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-3 rect,#mermaid-svg-O6zgy4U5hdqu7L6g .section-3 path,#mermaid-svg-O6zgy4U5hdqu7L6g .section-3 circle,#mermaid-svg-O6zgy4U5hdqu7L6g .section-3 polygon,#mermaid-svg-O6zgy4U5hdqu7L6g .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .section-3 text{fill:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .node-icon-3{font-size:40px;color:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-depth-3{stroke-width:5;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled circle,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:lightgray;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:#efefef;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-4 rect,#mermaid-svg-O6zgy4U5hdqu7L6g .section-4 path,#mermaid-svg-O6zgy4U5hdqu7L6g .section-4 circle,#mermaid-svg-O6zgy4U5hdqu7L6g .section-4 polygon,#mermaid-svg-O6zgy4U5hdqu7L6g .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .section-4 text{fill:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .node-icon-4{font-size:40px;color:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-depth-4{stroke-width:2;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled circle,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:lightgray;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:#efefef;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-5 rect,#mermaid-svg-O6zgy4U5hdqu7L6g .section-5 path,#mermaid-svg-O6zgy4U5hdqu7L6g .section-5 circle,#mermaid-svg-O6zgy4U5hdqu7L6g .section-5 polygon,#mermaid-svg-O6zgy4U5hdqu7L6g .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .section-5 text{fill:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .node-icon-5{font-size:40px;color:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-depth-5{stroke-width:-1;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled circle,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:lightgray;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:#efefef;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-6 rect,#mermaid-svg-O6zgy4U5hdqu7L6g .section-6 path,#mermaid-svg-O6zgy4U5hdqu7L6g .section-6 circle,#mermaid-svg-O6zgy4U5hdqu7L6g .section-6 polygon,#mermaid-svg-O6zgy4U5hdqu7L6g .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .section-6 text{fill:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .node-icon-6{font-size:40px;color:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-depth-6{stroke-width:-4;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled circle,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:lightgray;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:#efefef;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-7 rect,#mermaid-svg-O6zgy4U5hdqu7L6g .section-7 path,#mermaid-svg-O6zgy4U5hdqu7L6g .section-7 circle,#mermaid-svg-O6zgy4U5hdqu7L6g .section-7 polygon,#mermaid-svg-O6zgy4U5hdqu7L6g .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .section-7 text{fill:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .node-icon-7{font-size:40px;color:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-depth-7{stroke-width:-7;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled circle,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:lightgray;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:#efefef;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-8 rect,#mermaid-svg-O6zgy4U5hdqu7L6g .section-8 path,#mermaid-svg-O6zgy4U5hdqu7L6g .section-8 circle,#mermaid-svg-O6zgy4U5hdqu7L6g .section-8 polygon,#mermaid-svg-O6zgy4U5hdqu7L6g .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .section-8 text{fill:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .node-icon-8{font-size:40px;color:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-depth-8{stroke-width:-10;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled circle,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:lightgray;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:#efefef;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-9 rect,#mermaid-svg-O6zgy4U5hdqu7L6g .section-9 path,#mermaid-svg-O6zgy4U5hdqu7L6g .section-9 circle,#mermaid-svg-O6zgy4U5hdqu7L6g .section-9 polygon,#mermaid-svg-O6zgy4U5hdqu7L6g .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .section-9 text{fill:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .node-icon-9{font-size:40px;color:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-depth-9{stroke-width:-13;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled circle,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:lightgray;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:#efefef;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-10 rect,#mermaid-svg-O6zgy4U5hdqu7L6g .section-10 path,#mermaid-svg-O6zgy4U5hdqu7L6g .section-10 circle,#mermaid-svg-O6zgy4U5hdqu7L6g .section-10 polygon,#mermaid-svg-O6zgy4U5hdqu7L6g .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .section-10 text{fill:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .node-icon-10{font-size:40px;color:black;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .edge-depth-10{stroke-width:-16;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled circle,#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:lightgray;}#mermaid-svg-O6zgy4U5hdqu7L6g .disabled text{fill:#efefef;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-root rect,#mermaid-svg-O6zgy4U5hdqu7L6g .section-root path,#mermaid-svg-O6zgy4U5hdqu7L6g .section-root circle,#mermaid-svg-O6zgy4U5hdqu7L6g .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-O6zgy4U5hdqu7L6g .section-root text{fill:#ffffff;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-root span{color:#ffffff;}#mermaid-svg-O6zgy4U5hdqu7L6g .section-2 span{color:#ffffff;}#mermaid-svg-O6zgy4U5hdqu7L6g .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-O6zgy4U5hdqu7L6g .edge{fill:none;}#mermaid-svg-O6zgy4U5hdqu7L6g .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-O6zgy4U5hdqu7L6g :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 第2章 信息的表示和处理
信息存储基础
位 bit
字节 byte
十六进制 hex
字长 word size
字节顺序 大端小端
布尔代数和位运算
整数表示
无符号数 unsigned
补码 twos-complement
有符号 无符号 转换
扩展和截断
整数运算
无符号加法 模运算
补码加法 正负溢出
乘法 和 除法
乘以常数 移位优化
浮点数
二进制小数
IEEE 754 格式
规格化 非规格化 特殊值
舍入 rounding
浮点运算 不具结合性

逐个解释:

  • 信息存储基础:这是本章的"语言基础"。位(bit)是最小存储单位,8 个位组成一个字节(byte)。十六进制是二进制的一种"速记法"。字长决定了指针能访问多少内存(32 位→4GB,64 位→16EB)。字节顺序决定了多字节数据在内存中怎么排列(大端vs小端)。布尔代数和位运算是"直接操控二进制位"的工具。
  • 整数表示:无符号数就是纯二进制数;补码则用最高位表示负数(最高位是 1 就是负数)。它们可以互相转换(位模式不变、解释不同),这是 C 语言中无数 bug 的根源。
  • 整数运算:因为位数有限,加法会溢出(结果超出表示范围)。乘法更容易溢出,但编译器会用移位+加减法来优化。除以 2 的幂就是用右移来实现。
  • 浮点数 :用 V = (-1)^s × M × 2^E 的方式近似表示实数。规格化数按 IEEE 754 标准编码,非规格化数用来表示极小的数(逐渐下溢),特殊值表示无穷大(±∞)和 NaN。

3 核心知识点逐个讲 👩‍🏫


3.1 知识点 1:位、字节与十六进制

3.1.1 ① 什么是它(一句话)

(bit)是计算机存储信息的最小单位,只有 0 或 1 两种状态。字节 (byte)是 8 个位的组合,是大多数计算机寻址的最小单位。十六进制是二进制的"速记法",用 1 位十六进制对应 4 位二进制。

3.1.2 ② 大白话解释

想象一个巨大的储物柜,每个格子只能放一个小球,小球要么是黑的(0),要么是白的(1)。每个格子就是一个 。8 个格子排成一排就是一个字节

问题是:如果你要记录一个格子里是 10110111,光念出来就要半天。十六进制就是一道"翻译压缩"------把每 4 个格子(位)翻译成一个符号:

4位二进制 十六进制
0000 0
0001 1
... ...
1001 9
1010 A
1011 B
1100 C
1101 D
1110 E
1111 F

这样 10110111 就变成了 B7------从 8 个字符压缩到 2 个字符。在 C 语言中,0xB70XB7 表示十六进制常量。

生活例子 对应关系 它帮你理解什么 类比的边界
储物柜格子 格子 → 位(0或1),8个格子一排 → 字节 位是最小单位,字节是基本封装 真实格子物理上不会"溢出"
车牌号 每4位二进制 → 1位十六进制,就像"京A·12345"用6个字符表示完整信息 十六进制是二进制的缩写 车牌号不是纯数字编码
色卡编号 十六进制就是给每个4位模式起个名字(0~F),就像给每种颜色起个名字 记法转换,不改变"本质" 颜色命名有主观性,十六进制编码是确定的
3.1.3 ③ 为什么会有它
  • 如果没有位:无法表示--计算机底层电路只能识别高电压/低电压两种状态,所以"二进制"是唯一选择。
  • 如果没有字节:每次都要单独处理 1 个位,内存管理和数据传输效率极低。8 位一组刚好能编码 256 种不同值,可以放一个英文字母(ASCII)、一个颜色分量或一个较小的整数。
  • 如果没有十六进制 :二进制 1111001010110110110011 读起来像天书,写错一个位都很难发现。十六进制把"阅读长度"缩小 4 倍,极大降低人工阅读和调试的出错率。
3.1.4 ④ 工作流程 / 演进过程

#mermaid-svg-kt9kRx9RirbbCIaU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kt9kRx9RirbbCIaU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kt9kRx9RirbbCIaU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kt9kRx9RirbbCIaU .error-icon{fill:#552222;}#mermaid-svg-kt9kRx9RirbbCIaU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kt9kRx9RirbbCIaU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kt9kRx9RirbbCIaU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kt9kRx9RirbbCIaU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kt9kRx9RirbbCIaU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kt9kRx9RirbbCIaU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kt9kRx9RirbbCIaU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kt9kRx9RirbbCIaU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kt9kRx9RirbbCIaU .marker.cross{stroke:#333333;}#mermaid-svg-kt9kRx9RirbbCIaU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kt9kRx9RirbbCIaU p{margin:0;}#mermaid-svg-kt9kRx9RirbbCIaU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kt9kRx9RirbbCIaU .cluster-label text{fill:#333;}#mermaid-svg-kt9kRx9RirbbCIaU .cluster-label span{color:#333;}#mermaid-svg-kt9kRx9RirbbCIaU .cluster-label span p{background-color:transparent;}#mermaid-svg-kt9kRx9RirbbCIaU .label text,#mermaid-svg-kt9kRx9RirbbCIaU span{fill:#333;color:#333;}#mermaid-svg-kt9kRx9RirbbCIaU .node rect,#mermaid-svg-kt9kRx9RirbbCIaU .node circle,#mermaid-svg-kt9kRx9RirbbCIaU .node ellipse,#mermaid-svg-kt9kRx9RirbbCIaU .node polygon,#mermaid-svg-kt9kRx9RirbbCIaU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kt9kRx9RirbbCIaU .rough-node .label text,#mermaid-svg-kt9kRx9RirbbCIaU .node .label text,#mermaid-svg-kt9kRx9RirbbCIaU .image-shape .label,#mermaid-svg-kt9kRx9RirbbCIaU .icon-shape .label{text-anchor:middle;}#mermaid-svg-kt9kRx9RirbbCIaU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kt9kRx9RirbbCIaU .rough-node .label,#mermaid-svg-kt9kRx9RirbbCIaU .node .label,#mermaid-svg-kt9kRx9RirbbCIaU .image-shape .label,#mermaid-svg-kt9kRx9RirbbCIaU .icon-shape .label{text-align:center;}#mermaid-svg-kt9kRx9RirbbCIaU .node.clickable{cursor:pointer;}#mermaid-svg-kt9kRx9RirbbCIaU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kt9kRx9RirbbCIaU .arrowheadPath{fill:#333333;}#mermaid-svg-kt9kRx9RirbbCIaU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kt9kRx9RirbbCIaU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kt9kRx9RirbbCIaU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kt9kRx9RirbbCIaU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kt9kRx9RirbbCIaU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kt9kRx9RirbbCIaU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kt9kRx9RirbbCIaU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kt9kRx9RirbbCIaU .cluster text{fill:#333;}#mermaid-svg-kt9kRx9RirbbCIaU .cluster span{color:#333;}#mermaid-svg-kt9kRx9RirbbCIaU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kt9kRx9RirbbCIaU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kt9kRx9RirbbCIaU rect.text{fill:none;stroke-width:0;}#mermaid-svg-kt9kRx9RirbbCIaU .icon-shape,#mermaid-svg-kt9kRx9RirbbCIaU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kt9kRx9RirbbCIaU .icon-shape p,#mermaid-svg-kt9kRx9RirbbCIaU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kt9kRx9RirbbCIaU .icon-shape .label rect,#mermaid-svg-kt9kRx9RirbbCIaU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kt9kRx9RirbbCIaU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kt9kRx9RirbbCIaU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kt9kRx9RirbbCIaU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

二进制位模式
每4位一组
查表映射到 0-F
十六进制表示
需要还原?
在代码/调试器中使用

逐步说明

  1. 拿到的是一串 0 和 1(比如 1100100101111011)。
  2. 从右往左每 4 位分一组,最左边不够 4 位的补 0:1100 1001 0111 1011
  3. 每组查表:1100→C1001→90111→71011→B,得到 0xC97B
  4. 反过来,0x39A7F8 展开:3→00119→1001A→10107→0111F→11118→1000,得到二进制。

!example 🎬 动画脑补

把"十六进制转换"想象成一个翻译官。二进制是"位语言"------每个位自己在喊"我是0还是1"。翻译官把它们每 4 个编成一组,然后从兜里掏出一本"4位→符号"对照表,念出对应的符号(0~F)。反向翻译时,翻译官听到一个符号,就从表里翻出 4 个位的值,依次写下来。

3.1.5 ⑤ 图片说明(必须)

建议插入的图片

  • 十六进制与二进制/十进制的三栏对照表(0~F)
  • 一个字节(8位)拆分为两个十六进制数字的示意图
  • 2 的幂次在十进制、二进制、十六进制三种表示法下的对照表

为什么看这张图 :能一眼看出"一个十六进制数字精确对应 4 个二进制位"这个核心关系,以及 2^10=1024(约 1K)、2^20≈1M 这种常用的对应。

可用于后续绘图的描述:一张横向表格,列出 2^0 到 2^15 的十进制值、二进制表示(1 后面跟 n 个 0)、十六进制表示(当 n=4k+i 时,开头为 1/2/4/8,后面跟 k 个 0)。

3.1.6 ⑥ 容易混淆的概念
对比维度 十六进制 十进制 二进制
是什么 以16为基数的记数法,用0-9和A-F 以10为基数的记数法,日常使用 以2为基数的记数法,只用0和1
解决什么问题 给二进制做"速记",方便人阅读 符合人类10根手指的习惯 符合电路二值状态的物理实现
在计算机中怎么用 调试器、内存dump、汇编代码中表示地址和数据 高层应用中显示给人看的数值 机器的"母语",一切数据的最终形态
一个例子 0x7AF = 1967(十进制) 1967(千位1+百位9+十位6+个位7) 0111 1010 1111
3.1.7 ⑦ 面试会怎么问 / 怎么向别人讲

问题 :给你一个十六进制数 0xD5E4C,请转换为二进制。

回答 :把每个十六进制数字分别展开成 4 位二进制:D=11015=0101E=11104=0100C=1100,拼接得到 1101 0101 1110 0100 1100。反过来也一样,把二进制每 4 位一组映射到十六进制即可。

3.1.8 ⑧ 一句话总结

!note

十六进制是二进制的"压缩表示法"------每 1 位十六进制精确对应 4 位二进制,是人阅读机器位模式的桥梁。

3.1.9 知识点 1 自测 🧠
3.1.9.1 基础题(5 题)
  1. 1 个字节有多少位?
  2. 十六进制数字 F 对应的十进制值和二进制值分别是什么?
  3. 0x39A7F8 转换为二进制。
  4. 将二进制 1100 1001 0111 1011 转换为十六进制。
  5. C 语言中十六进制常数以什么前缀开头?
3.1.9.2 理解题(5 题)
  1. 为什么十六进制比十进制更适合作为二进制的"速记法"?
  2. 为什么计算机使用二进制而不是十进制来存储信息?
  3. 0x7AF 对应的十进制值是多少?写出计算过程。
  4. 2^10 和 2^20 的十六进制表示分别是什么?
  5. 为什么 1 个字节的值域是 0x000xFF
3.1.9.3 思考题(3 题)
  1. 如果有一种新计算机使用三进制(每位可以是 0、1、2),1 个"三进制字节"(比如 6 个三进制位)能表示多少种不同的值?
  2. 不使用计算器,如何快速判断 0x1A3F6719(十进制)哪个更大?
  3. 为什么 2^n 转换成十六进制特别简单?总结规律。
3.1.9.4 面试/表达题(5 题)
  1. 请口头完成 0x3F7 到二进制的转换,并解释规则。
  2. 请口头完成二进制 1011 0110 到十六进制的转换。
  3. C 语言中 0x1F31037(八进制)表示的是同一个值吗?为什么?
  4. 一个 32 位的 IP 地址(如 192.168.1.1)如何在内存中用十六进制表示?
  5. 为什么调试器中内存内容通常以十六进制显示而不是十进制?

点击查看答案

基础题

  1. 8 位。
  2. F = 15(十进制)= 1111(二进制)。
  3. 0x39A7F8 = 0011 1001 1010 0111 1111 1000
  4. 1100 1001 0111 1011 = 0xC97B
  5. 0x0X

理解题

  1. 因为 16 = 2^4,每 4 位二进制恰好对应 1 位十六进制,是齐整的映射关系。而 10 不是 2 的幂,十进制和二进制之间没有简单的分组对应。
  2. 因为物理电路最可靠地区分两种状态(高电压/低电压、有磁/无磁),而区分 10 种状态的电路要复杂得多且更容易出错。
  3. 0x7AF = 7×256 + 10×16 + 15×1 = 1792 + 160 + 15 = 1967。
  4. 2^10 = 1024 = 0x400(1 后面跟 10 个 0 的二进制 → 2 个十六进制 0 不够 4 位补齐 → 0x400);2^20 = 0x100000
  5. 1 字节 = 8 位,最小值 0000 0000 = 0x00,最大值 1111 1111 = 0xFF(=255)。

思考题

  1. 6 个三进制位 = 3^6 = 729 种不同的值。
  2. 0x1A3F = 1×4096 + 10×256 + 3×16 + 15 = 4096 + 2560 + 48 + 15 = 6719,两者相等。
  3. 2^n:当 n = 4k+i (0≤i<4) 时,二进制是 1 后面跟 n 个 0,十六进制开头为 1(i=0)、2(i=1)、4(i=2)、8(i=3),后面跟 k 个 0。

面试/表达题

  1. 3→0011F→11117→0111,拼接为 0011 1111 0111。规则:每个十六进制数字用 4 位二进制替换。
  2. 1011→B0110→6,结果为 0xB6
  3. 是同一个值(都是 31)。0x 表示十六进制,0 开头表示八进制(037 = 3×8+7 = 31)。但注意:八进制在现代 C 编程中应尽量避免使用。
  4. 192.168.1.10xC0A80101(每个十进制段转为十六进制:192=C0, 168=A8, 1=01, 1=01)。
  5. 因为十六进制和二进制之间的转换是 N:1 映射(4位↔1位),一眼就能看出位模式;而十进制和二进制之间没有简单对应关系。

3.2 知识点 2:字长(Word Size)与数据大小

3.2.1 ① 什么是它(一句话)

字长(word size)是计算机的"标称尺寸"------它决定了指针的大小,从而决定了虚拟地址空间的最大范围(32 位→4GB,64 位→16EB)。

3.2.2 ② 大白话解释

计算机就像一个城市,内存就是城市里的房子。每个房子都有一个门牌号(地址)。字长决定了门牌号最长能是多少位------32 位机器上,门牌号从 0 到 4,294,967,295(约第 43 亿号),所以最多能访问 4GB 内存。64 位机器上门牌号能到 16EB(1EB = 10 亿 GB),大到几乎用不完。

但"字长"不止影响地址。C 语言的不同数据类型在不同字长下的字节数也不一样:

C 声明 32 位程序 64 位程序
char 1 字节 1 字节
short 2 字节 2 字节
int 4 字节 4 字节
long 4 字节 8 字节
char *(指针) 4 字节 8 字节
float 4 字节 4 字节
double 8 字节 8 字节

long 和指针的大小会随字长变化------这就是为什么在 32 位机器上"能跑"的代码,移植到 64 位可能崩。

生活例子 对应关系 它帮你理解什么 类比的边界
城市门牌号 门牌号位数 → 字长,门牌号 → 地址,房屋 → 字节 字长决定地址空间上限 真实城市不可能有 16EB 个房子
快递单号 快递单号的长度决定了最多能有多少个快递 字长决定了地址范围 快递单号不是二进制
电话号码位数 7 位号码 → 最多 1000 万个号码,8 位 → 最多 1 亿个 字长翻倍,地址空间指数增长 电话号码不是 2 的幂次递增
3.2.3 ③ 为什么会有它
  • 如果没有字长概念:程序无法知道指针占几个字节,也就无法正确访问内存。编译器需要知道"指针多宽"才能生成正确的机器码。
  • 从 32 位迁移到 64 位的原因:4GB 已经不够用了。现代浏览器、大型游戏、数据库都需要远超 4GB 的内存。64 位一下子把天花板从 4GB 提到了 16EB。
  • 向后兼容 :64 位机器能跑 32 位程序(用 gcc -m32 编译),这让过渡期变得平滑。但这意味着要同时理解两种字长下的程序行为。
3.2.4 ④ 工作流程 / 演进过程

#mermaid-svg-5rrVQmeWCCRK118H{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5rrVQmeWCCRK118H .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5rrVQmeWCCRK118H .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5rrVQmeWCCRK118H .error-icon{fill:#552222;}#mermaid-svg-5rrVQmeWCCRK118H .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5rrVQmeWCCRK118H .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5rrVQmeWCCRK118H .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5rrVQmeWCCRK118H .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5rrVQmeWCCRK118H .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5rrVQmeWCCRK118H .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5rrVQmeWCCRK118H .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5rrVQmeWCCRK118H .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5rrVQmeWCCRK118H .marker.cross{stroke:#333333;}#mermaid-svg-5rrVQmeWCCRK118H svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5rrVQmeWCCRK118H p{margin:0;}#mermaid-svg-5rrVQmeWCCRK118H .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5rrVQmeWCCRK118H .cluster-label text{fill:#333;}#mermaid-svg-5rrVQmeWCCRK118H .cluster-label span{color:#333;}#mermaid-svg-5rrVQmeWCCRK118H .cluster-label span p{background-color:transparent;}#mermaid-svg-5rrVQmeWCCRK118H .label text,#mermaid-svg-5rrVQmeWCCRK118H span{fill:#333;color:#333;}#mermaid-svg-5rrVQmeWCCRK118H .node rect,#mermaid-svg-5rrVQmeWCCRK118H .node circle,#mermaid-svg-5rrVQmeWCCRK118H .node ellipse,#mermaid-svg-5rrVQmeWCCRK118H .node polygon,#mermaid-svg-5rrVQmeWCCRK118H .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5rrVQmeWCCRK118H .rough-node .label text,#mermaid-svg-5rrVQmeWCCRK118H .node .label text,#mermaid-svg-5rrVQmeWCCRK118H .image-shape .label,#mermaid-svg-5rrVQmeWCCRK118H .icon-shape .label{text-anchor:middle;}#mermaid-svg-5rrVQmeWCCRK118H .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5rrVQmeWCCRK118H .rough-node .label,#mermaid-svg-5rrVQmeWCCRK118H .node .label,#mermaid-svg-5rrVQmeWCCRK118H .image-shape .label,#mermaid-svg-5rrVQmeWCCRK118H .icon-shape .label{text-align:center;}#mermaid-svg-5rrVQmeWCCRK118H .node.clickable{cursor:pointer;}#mermaid-svg-5rrVQmeWCCRK118H .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5rrVQmeWCCRK118H .arrowheadPath{fill:#333333;}#mermaid-svg-5rrVQmeWCCRK118H .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5rrVQmeWCCRK118H .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5rrVQmeWCCRK118H .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5rrVQmeWCCRK118H .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5rrVQmeWCCRK118H .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5rrVQmeWCCRK118H .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5rrVQmeWCCRK118H .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5rrVQmeWCCRK118H .cluster text{fill:#333;}#mermaid-svg-5rrVQmeWCCRK118H .cluster span{color:#333;}#mermaid-svg-5rrVQmeWCCRK118H div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5rrVQmeWCCRK118H .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5rrVQmeWCCRK118H rect.text{fill:none;stroke-width:0;}#mermaid-svg-5rrVQmeWCCRK118H .icon-shape,#mermaid-svg-5rrVQmeWCCRK118H .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5rrVQmeWCCRK118H .icon-shape p,#mermaid-svg-5rrVQmeWCCRK118H .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5rrVQmeWCCRK118H .icon-shape .label rect,#mermaid-svg-5rrVQmeWCCRK118H .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5rrVQmeWCCRK118H .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5rrVQmeWCCRK118H .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5rrVQmeWCCRK118H :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} m32
m64
编译器决定目标字长
编译选项
32位程序: 指针=4字节, long=4字节
64位程序: 指针=8字节, long=8字节
最大地址空间 4GB
最大地址空间 16EB

3.2.5 ⑤ 图片说明

建议插入的图片

  • 32 位 vs 64 位程序的地址空间对比图(4GB vs 16EB)
  • 各 C 数据类型在 32 位和 64 位下的字节大小对比表
3.2.6 ⑥ 容易混淆的概念
对比维度 32 位程序 64 位程序 最关键区别
是什么 -m32 编译,可在 32 位或 64 位机器上运行 -m64 编译,只能在 64 位机器上运行 编译选项决定,不是机器决定
指针大小 4 字节 8 字节 影响所有指针运算
long 大小 4 字节 8 字节 longint 在 32 位下等价,64 位下不等
地址空间 最多 4GB 最多 16EB 64 位突破了 4GB 瓶颈
3.2.7 ⑦ 面试会怎么问

问题int 在 32 位和 64 位下分别是几个字节?long 呢?

回答int 在两种情况下通常都是 4 字节。long 在 32 位下是 4 字节,在 64 位下是 8 字节。这是最常见的移植问题------在 32 位下用 long 存指针没问题,但搬到 64 位后 long 变成了 8 字节,可能导致意外行为。如果要精确控制大小,请用 int32_tint64_t

3.2.8 ⑧ 一句话总结

!note

字长决定了指针有多大、能访问多少内存。int32_tint64_t 等定长类型让程序员可以摆脱"典型值"的依赖,写出可移植代码。

3.2.9 知识点 2 自测 🧠
3.2.9.1 基础题(5 题)
  1. 32 位程序的最大虚拟地址空间是多少?
  2. C 语言中 char 数据类型占几个字节?
  3. int32_t 占几个字节?(提示:看名字)
  4. 64 位程序中,指针变量占几个字节?
  5. C 语言中声明一个无符号的 32 位整数应使用什么类型名?
3.2.9.2 理解题(5 题)
  1. 为什么 32 位程序的地址空间上限是 4GB?
  2. 为什么 int 在 64 位机器上通常还是 4 字节,而不是 8 字节?
  3. 如果一个 32 位程序中用 int 存了一个指针值,移植到 64 位后会发生什么?
  4. unsigned longuint64_t 有什么区别?
  5. 大多数 64 位机器能运行 32 位程序,这种能力叫什么?
3.2.9.3 思考题(3 题)
  1. 如果未来出现 128 位字长的机器,理论上能访问多少内存?
  2. 为什么 floatdouble 的字节数不随字长改变?
  3. sizeof(char) 在 C 语言标准中始终等于 1,这意味着什么?
3.2.9.4 面试/表达题(5 题)
  1. 解释 32 位和 64 位程序的主要区别及迁移中常见的坑。
  2. size_t 是什么?它在 32 位和 64 位下分别是几个字节?
  3. 你如何确保一个整型变量在 32 位和 64 位下都有相同的大小?
  4. 为什么 GCC 编译选项 -m32 能让 64 位机器产生 32 位代码?
  5. 解释 long 在 32 位 Linux、64 位 Linux 和 64 位 Windows 上的大小差异。

点击查看答案

基础题

  1. 4GB(2^32 字节)。
  2. 1 字节。
  3. 4 字节(32 位 ÷ 8 位/字节 = 4 字节)。
  4. 8 字节(64 位 ÷ 8 = 8 字节)。
  5. uint32_t

理解题

  1. 每个地址是 32 位,最多有 2^32 个不同地址,每个地址指向 1 字节,所以最多 2^32 字节 = 4GB。
  2. 历史兼容性------大量现存代码假设 int 是 4 字节。把 int 改为 8 字节会破坏大量程序。所以让 long 变长来提供 8 字节整数。
  3. 在 32 位下,指针 4 字节,int 也是 4 字节,可以互转。64 位下指针变成 8 字节,但 int 仍是 4 字节,存指针值会截断高 4 字节,导致野指针/崩溃。
  4. unsigned long 的大小取决于平台(32 位→4 字节,64 位 Linux→8 字节,64 位 Windows→4 字节)。uint64_t 明确保证 8 字节,跨平台一致。
  5. 向后兼容(backward compatibility)。

思考题

  1. 2^128 字节 ≈ 3.4×10^38 字节 ≈ 340 亿亿亿亿 GB,远超可观测宇宙的原子总数。
  2. floatdouble 的大小是由 IEEE 754 标准规定的(4 字节和 8 字节),与机器字长无关。它们有自己的精度/范围设计,不需要随字长扩展。
  3. sizeof 的单位是"char 的大小",所以 sizeof(char) 始终为 1。这意味着 sizeof 返回的是"需要多少个 char 才能存下该类型"。

面试/表达题

  1. 主要区别:指针和 long 的大小。常见坑:用 int 存指针值、sizeof(long) != sizeof(int) 的假设、malloc 参数的类型 size_t 大小变化。
  2. size_tsizeof 返回值的类型,无符号。32 位→4 字节,64 位→8 字节。
  3. 使用 int32_tint64_tuint32_tuint64_t<stdint.h> 中定义的定长类型。
  4. 编译器后端支持生成 32 位指令、使用 32 位 ABI(应用程序二进制接口),让 64 位 CPU 以兼容模式运行 32 位代码。
  5. 32 位 Linux:4 字节。64 位 Linux(LP64 模型):8 字节。64 位 Windows(LLP64 模型):4 字节。这是一个经典的跨平台差异。

3.3 知识点 3:字节顺序------大端法与小端法

3.3.1 ① 什么是它(一句话)

大端法 (big-endian)把最高有效字节存在最小地址;小端法(little-endian)把最低有效字节存在最小地址。同一个多字节数字在不同机器上,内存排列可能"颠倒"。

3.3.2 ② 大白话解释

假设你有一个 4 字节的整数 0x12345678(十六进制),要存到内存地址 0x100 到 0x103 这四个字节中。

大端法 认为"最重要的应该排在最前面"------最高字节 0x12 存在地址 0x100,然后是 0x340x560x78。读出来就是 12 34 56 78,跟写出来的顺序一模一样。

小端法 认为"最小的先来"------最低字节 0x78 存在地址 0x100,然后依次 0x560x340x12。读出来是 78 56 34 12,跟书写顺序完全颠倒。

地址 大端法 小端法
0x100 12 78
0x101 34 56
0x102 56 34
0x103 78 12

大多数 Intel 兼容机使用小端法(所以你的 PC 很可能就是小端法)。网络传输通常使用大端法("网络字节序")。

生活例子 对应关系 它帮你理解什么 类比的边界
读数字的习惯 中文"一百二十三"先说百位(大端);英文"one hundred twenty-three"也是大端。但念阿拉伯数字时我们从左(高位)往右(低位)读 大端像我们写数字的习惯,先写最高位 人类习惯大端,但机器的"自然实现"往往偏小端
字母排列 大端像"ABCD",小端像"DCBA"存到相同的格子里 同样四个字母,排列方式可以完全不同 字母排序有语义,字节排序纯粹是约定
快递包裹编号 大端:省份→城市→区→街道(粗到细)。小端:街道→区→城市→省份 两种"分层"方式各有道理 包裹编号有层次语义,字节只是一个平的数字
3.3.3 ③ 为什么会有它
  • 为什么会有两种:不是谁对谁错,而是不同设计者选了不同的"方便"。小端法在硬件实现上有优势------做加法时先处理低位、进位往高位传,跟"小端先到"的顺序一致。大端法对人阅读更友好------内存dump从左往右看过去,数字也是从左往右的。
  • 如果没有统一约定 :两台不同字节序的机器通过网络传数据时,接收方会把 0x12345678 读成 0x78563412。这就是为什么网络协议(如 TCP/IP)定义了"网络字节序"(大端),发送前必须转换。
3.3.4 ④ 工作流程 / 演进过程

#mermaid-svg-0tdsAWSWSqmlwH2F{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0tdsAWSWSqmlwH2F .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0tdsAWSWSqmlwH2F .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0tdsAWSWSqmlwH2F .error-icon{fill:#552222;}#mermaid-svg-0tdsAWSWSqmlwH2F .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0tdsAWSWSqmlwH2F .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0tdsAWSWSqmlwH2F .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0tdsAWSWSqmlwH2F .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0tdsAWSWSqmlwH2F .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0tdsAWSWSqmlwH2F .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0tdsAWSWSqmlwH2F .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0tdsAWSWSqmlwH2F .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0tdsAWSWSqmlwH2F .marker.cross{stroke:#333333;}#mermaid-svg-0tdsAWSWSqmlwH2F svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0tdsAWSWSqmlwH2F p{margin:0;}#mermaid-svg-0tdsAWSWSqmlwH2F .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0tdsAWSWSqmlwH2F .cluster-label text{fill:#333;}#mermaid-svg-0tdsAWSWSqmlwH2F .cluster-label span{color:#333;}#mermaid-svg-0tdsAWSWSqmlwH2F .cluster-label span p{background-color:transparent;}#mermaid-svg-0tdsAWSWSqmlwH2F .label text,#mermaid-svg-0tdsAWSWSqmlwH2F span{fill:#333;color:#333;}#mermaid-svg-0tdsAWSWSqmlwH2F .node rect,#mermaid-svg-0tdsAWSWSqmlwH2F .node circle,#mermaid-svg-0tdsAWSWSqmlwH2F .node ellipse,#mermaid-svg-0tdsAWSWSqmlwH2F .node polygon,#mermaid-svg-0tdsAWSWSqmlwH2F .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0tdsAWSWSqmlwH2F .rough-node .label text,#mermaid-svg-0tdsAWSWSqmlwH2F .node .label text,#mermaid-svg-0tdsAWSWSqmlwH2F .image-shape .label,#mermaid-svg-0tdsAWSWSqmlwH2F .icon-shape .label{text-anchor:middle;}#mermaid-svg-0tdsAWSWSqmlwH2F .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0tdsAWSWSqmlwH2F .rough-node .label,#mermaid-svg-0tdsAWSWSqmlwH2F .node .label,#mermaid-svg-0tdsAWSWSqmlwH2F .image-shape .label,#mermaid-svg-0tdsAWSWSqmlwH2F .icon-shape .label{text-align:center;}#mermaid-svg-0tdsAWSWSqmlwH2F .node.clickable{cursor:pointer;}#mermaid-svg-0tdsAWSWSqmlwH2F .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0tdsAWSWSqmlwH2F .arrowheadPath{fill:#333333;}#mermaid-svg-0tdsAWSWSqmlwH2F .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0tdsAWSWSqmlwH2F .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0tdsAWSWSqmlwH2F .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0tdsAWSWSqmlwH2F .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0tdsAWSWSqmlwH2F .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0tdsAWSWSqmlwH2F .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0tdsAWSWSqmlwH2F .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0tdsAWSWSqmlwH2F .cluster text{fill:#333;}#mermaid-svg-0tdsAWSWSqmlwH2F .cluster span{color:#333;}#mermaid-svg-0tdsAWSWSqmlwH2F div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0tdsAWSWSqmlwH2F .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0tdsAWSWSqmlwH2F rect.text{fill:none;stroke-width:0;}#mermaid-svg-0tdsAWSWSqmlwH2F .icon-shape,#mermaid-svg-0tdsAWSWSqmlwH2F .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0tdsAWSWSqmlwH2F .icon-shape p,#mermaid-svg-0tdsAWSWSqmlwH2F .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0tdsAWSWSqmlwH2F .icon-shape .label rect,#mermaid-svg-0tdsAWSWSqmlwH2F .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0tdsAWSWSqmlwH2F .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0tdsAWSWSqmlwH2F .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0tdsAWSWSqmlwH2F :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 大端法
小端法
整数 0x12345678
存到内存
地址0x100: 12

地址0x101: 34

地址0x102: 56

地址0x103: 78
地址0x100: 78

地址0x101: 56

地址0x102: 34

地址0x103: 12
网络传输前

需转为大端
Intel/AMD

默认存储

!example 🎬 动画脑补

有两个人排队进电梯。电梯有四层(四个地址格子),每层只能站一个人(一个字节)。

大端法:让"地位最高"的队长 12 先进入一层,然后是 345678

小端法:让"年纪最小"的队员 78 先进入一层,然后是 563412

电梯到了对方面前,如果对方不知道是用哪种方式排的队,就会把队长认成队员、队员认成队长。

3.3.5 ⑤ 图片说明

建议插入的图片

  • 大端法和小端法的对比图:同一个 4 字节整数,两种排列方式的地址→值对应关系
  • show_bytes 程序的输出示例:在大端和小端机器上打印同一个整数的字节序列

为什么看这张图:直观看到"同一数字在不同机器上字节排列完全颠倒",这是网络编程和二进制文件格式必须面对的问题。

3.3.6 ⑥ 容易混淆的概念
对比维度 大端法 小端法 最关键区别
是什么 最高有效字节存在最小地址 最低有效字节存在最小地址 "谁先被存"的规则相反
常见平台 Sun SPARC、PowerPC、网络字节序 Intel x86/x64、ARM(可配置) PC 几乎都是小端
阅读友好性 内存 dump 和书写顺序一致 内存 dump 和书写顺序颠倒 大端对人友好
硬件实现 需额外处理 低位先到,和进位方向一致 小端对硬件友好
3.3.7 ⑦ 面试会怎么问

问题:如何用 C 代码判断当前机器是大端法还是小端法?

回答 :定义一个 int x = 1,然后用 char *p = (char *)&x 取第一个字节。如果 *p == 1 则是小端法(低位字节 0x01 在低地址),否则是大端法。这在家庭作业 2.58 中有详细讨论。

3.3.8 ⑧ 一句话总结

!note

大端法"高位放低地址",小端法"低位放低地址"。你的 PC 极有可能是小端法,但网络通信必须注意字节序转换。

3.3.9 知识点 3 自测 🧠
3.3.9.1 基础题(5 题)
  1. 大端法的"大"指的是什么?
  2. 0x12345678 在小端法机器上,最低地址的字节(第一个字节)的值是多少?
  3. C 语言中的 show_bytes 函数的作用是什么?
  4. "网络字节序"使用的是大端法还是小端法?
  5. 同一个数字,在大端法和小端法机器上,位模式(bit pattern)是否相同?
3.3.9.2 理解题(5 题)
  1. 为什么说"阅读大端法机器的内存 dump 更方便"?
  2. show_bytes 打印字符串 "12345",在大端和小端机器上结果一样吗?为什么?
  3. 为什么 C 语言的 union 或指针强制类型转换可以用来检测字节序?
  4. 0x87654321 的低 2 字节在小端法、大端法下,show_bytes 分别输出什么?
  5. 两台不同字节序的机器通过 Socket 传 int 数据时会发生什么?如何避免?
3.3.9.3 思考题(3 题)
  1. 如果一个处理器支持运行时切换大端/小端模式,什么时候会用到这种能力?
  2. 为什么 ARM 处理器被设计成可以配置字节序而 x86 是固定的小端?
  3. 单字节数据(如 char、字符串)有字节序问题吗?为什么?
3.3.9.4 面试/表达题(5 题)
  1. 口头解释大端法和小端法的区别,并给出一个具体例子。
  2. 你在 gdb 中看到了内存 78 56 34 12,原始 32 位整数在小端和大端机器上分别是多少?
  3. 什么是"字节序",为什么它只在多字节数据类型时有意义?
  4. C 语言读写二进制文件时,如何保证跨平台的可移植性?
  5. htons()htonl()ntohs()ntohl() 这些函数的作用和命名含义?

点击查看答案

基础题

  1. "大"指最高有效字节(most significant byte,MSB)存放在最低地址,即"大"的先来。
  2. 0x78(最低有效字节在最低地址)。
  3. 以字节为单位打印任意程序对象的字节序列,用于观察底层位模式。
  4. 大端法。
  5. 位模式(所有位)相同,但位的空间分布(字节排列顺序)不同。位模式在 CPU 寄存器中一样,但在内存中字节排列不同。

理解题

  1. 内存地址从左到右递增,大端法把高位字节放左边,跟人书写数字的习惯一致,所以 12 34 56 78 直接就是 0x12345678
  2. 一样。字符串每个字符就是一个字节('1'=0x31'2'=0x32......),单字节没有字节序问题。而且 ASCII 码对所有系统通用。
  3. 联合体(union)让同一内存区域可以按不同方式解释;指针强制转换 (char *) &x 让程序按字节访问多字节数据的第一个字节,从而判断低位是否存在低地址。
  4. 小端法:21 43(低位在前);大端法:87 65(高位在前)。
  5. 接收方会把 0x12345678 读成 0x78563412。解决:发送方用 htonl() 转为网络字节序(大端),接收方用 ntohl() 转回本机字节序。

思考题

  1. 仿真不同架构的程序、运行虚拟机时模拟目标平台的字节序、调试网络协议时切换字节序以测试代码的健壮性。
  2. x86 从 8086 时代就确定了小端,几十年累积的软件生态不可能再改。ARM 作为后来者,为了在嵌入式/网络设备中灵活应用,设计成了可配置的(但 Android/iOS 实际都用小端模式)。
  3. 没有。单字节数据只有一个字节,不存在"先存哪个字节"的问题。字符串实际上是字符数组,每个字符是一个字节,所以也没有字节序问题------这也是文本数据比二进制数据更具平台独立性的根本原因。

面试/表达题

  1. 大端:0x12345678 存为 12 34 56 78(高位在低地址);小端:0x12345678 存为 78 56 34 12(低位在低地址)。Intel/AMD 是小端,网络传输是大端。
  2. 小端→0x12345678(低地址 0x78);大端→0x78563412(低地址 0x78 是最低字节)。注意:需要知道数据的原始值才能确定哪个是正确的。
  3. 字节序是"多字节数据内部各字节在内存中的排列顺序"。单字节没有内部排列一说。
  4. 写入前将多字节整数转为固定字节序(通常网络字节序),读取时按同样规则转回。或者使用文本格式替代二进制格式。
  5. hton = host to network(本机→网络),ntoh = network to host(网络→本机),s = short(16 位),l = long(32 位)。它们将 16 位/32 位整数在本机字节序和网络字节序(大端)之间转换。

3.4 知识点 4:布尔代数与 C 语言位运算

3.4.1 ① 什么是它(一句话)

布尔代数 是用 1(真)和 0(假)进行的逻辑运算。C 语言位级运算&|~^)直接在二进制位的层面进行操作,是底层编程最基础的工具。

3.4.2 ② 大白话解释

想象每个位是一个小开关(ON=1,OFF=0)。四种基本操作:

  • &(AND,与) :"两个开关都 ON 才 ON"。0&0=00&1=01&0=01&1=1。常用于"掩码"------用一个全是 1 的模式把想保留的位"筛出来"。
  • |(OR,或):"任意一个 ON 就 ON"。常用于"置位"------把指定的几个位强行设为 1。
  • ~(NOT,取反) :"ON 变 OFF,OFF 变 ON"。~0=1~1=0
  • ^(XOR,异或) :"两个不同才 ON"。0^0=00^1=11^0=11^1=0。妙用:a^a=0,所以可以用异或实现不借助临时变量的交换。
生活例子 对应关系 它帮你理解什么 类比的边界
两道门的房间 AND:两道门都锁着才能进 → 两个位都是 1 结果才 1 与运算的要求"两个都满足" 门锁有无穷多种,位只有两种状态
两条路的交叉口 OR:任意一条路可通行就能到达 → 任意一个位是 1 结果就是 1 或运算的"任一满足即可" 路口通行不是二值的
开关+反相器 NOT:开关按下→灯灭,松开→灯亮 取反操作翻转状态 实际电路有延迟
3.4.3 ③ 为什么会有它
  • 如果没有位运算 :无法直接操控硬件寄存器(比如设置 GPIO 引脚)、无法实现高效的位图(bitmap)、无法做权限掩码(如 Linux 文件权限 rwx 用 3 位表示)。
  • 为什么用位向量表示集合 :一个 w 位的位向量可以表示 {0, 1, ..., w-1} 的任意子集。a & b 就是交集,a | b 就是并集,~a 就是补集。这在信号掩码、权限系统中大量使用。
  • 为什么编译器喜欢位运算:位运算是最快的 CPU 指令之一(通常 1 个时钟周期),乘法可能需要 3 个以上。
3.4.4 ④ 工作流程 / 演进过程

#mermaid-svg-RrGhdwszxJebwD4P{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RrGhdwszxJebwD4P .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RrGhdwszxJebwD4P .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RrGhdwszxJebwD4P .error-icon{fill:#552222;}#mermaid-svg-RrGhdwszxJebwD4P .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RrGhdwszxJebwD4P .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RrGhdwszxJebwD4P .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RrGhdwszxJebwD4P .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RrGhdwszxJebwD4P .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RrGhdwszxJebwD4P .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RrGhdwszxJebwD4P .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RrGhdwszxJebwD4P .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RrGhdwszxJebwD4P .marker.cross{stroke:#333333;}#mermaid-svg-RrGhdwszxJebwD4P svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RrGhdwszxJebwD4P p{margin:0;}#mermaid-svg-RrGhdwszxJebwD4P .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RrGhdwszxJebwD4P .cluster-label text{fill:#333;}#mermaid-svg-RrGhdwszxJebwD4P .cluster-label span{color:#333;}#mermaid-svg-RrGhdwszxJebwD4P .cluster-label span p{background-color:transparent;}#mermaid-svg-RrGhdwszxJebwD4P .label text,#mermaid-svg-RrGhdwszxJebwD4P span{fill:#333;color:#333;}#mermaid-svg-RrGhdwszxJebwD4P .node rect,#mermaid-svg-RrGhdwszxJebwD4P .node circle,#mermaid-svg-RrGhdwszxJebwD4P .node ellipse,#mermaid-svg-RrGhdwszxJebwD4P .node polygon,#mermaid-svg-RrGhdwszxJebwD4P .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RrGhdwszxJebwD4P .rough-node .label text,#mermaid-svg-RrGhdwszxJebwD4P .node .label text,#mermaid-svg-RrGhdwszxJebwD4P .image-shape .label,#mermaid-svg-RrGhdwszxJebwD4P .icon-shape .label{text-anchor:middle;}#mermaid-svg-RrGhdwszxJebwD4P .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RrGhdwszxJebwD4P .rough-node .label,#mermaid-svg-RrGhdwszxJebwD4P .node .label,#mermaid-svg-RrGhdwszxJebwD4P .image-shape .label,#mermaid-svg-RrGhdwszxJebwD4P .icon-shape .label{text-align:center;}#mermaid-svg-RrGhdwszxJebwD4P .node.clickable{cursor:pointer;}#mermaid-svg-RrGhdwszxJebwD4P .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RrGhdwszxJebwD4P .arrowheadPath{fill:#333333;}#mermaid-svg-RrGhdwszxJebwD4P .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RrGhdwszxJebwD4P .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RrGhdwszxJebwD4P .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RrGhdwszxJebwD4P .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RrGhdwszxJebwD4P .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RrGhdwszxJebwD4P .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RrGhdwszxJebwD4P .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RrGhdwszxJebwD4P .cluster text{fill:#333;}#mermaid-svg-RrGhdwszxJebwD4P .cluster span{color:#333;}#mermaid-svg-RrGhdwszxJebwD4P div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RrGhdwszxJebwD4P .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RrGhdwszxJebwD4P rect.text{fill:none;stroke-width:0;}#mermaid-svg-RrGhdwszxJebwD4P .icon-shape,#mermaid-svg-RrGhdwszxJebwD4P .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RrGhdwszxJebwD4P .icon-shape p,#mermaid-svg-RrGhdwszxJebwD4P .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RrGhdwszxJebwD4P .icon-shape .label rect,#mermaid-svg-RrGhdwszxJebwD4P .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RrGhdwszxJebwD4P .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RrGhdwszxJebwD4P .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RrGhdwszxJebwD4P :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 两个操作数
按位执行运算
每位独立计算
组合成结果

0x69 & 0x55 为例:

  • 0x69 = 0110 1001
  • 0x55 = 0101 0101
  • 按位 AND:0100 0001 = 0x41

!example 🎬 动画脑补

两队人面对面站着,每个人手里举着一个牌子(0 或 1)。AND 运算就是"两人都举 1 的,记录为 1;否则记录为 0"。OR 运算就是"至少一人举 1 的,记录为 1"。XOR 就是"两人举的不一样,记录为 1"。每对人独立计算,最后得到的牌子序列就是结果。

3.4.5 ⑤ 图片说明

建议插入的图片

  • 四种布尔运算的真值表(AND、OR、NOT、XOR)
  • 两个位向量做位运算的并行示意图
  • 掩码运算示例图:原始数据、掩码、结果三层对比
3.4.6 ⑥ 容易混淆的概念

| 对比维度 | 位级运算 & | | 逻辑运算 && || | 最关键区别 |

| --- | --- | --- | --- |

| 是什么 | 对每一位做布尔运算 | 对整个值的"真/假"做判断 | 粒度不同:按位 vs 整体 |

| 返回值 | 位模式(多位的值) | 只能是 1 或 0 | 位运算返回多位数,逻辑运算返回 0 或 1 |

| 短路求值 | 不短路,两边都算 | &&|| 有短路:如果左边已能确定结果,右边不执行 | 逻辑运算有短路特性 |

| 典型用法 | flags & MASKx | (1<<3) | if (p && *p)a>0 && b/a>1 | 位运算操作位,逻辑运算控制流程 |

3.4.7 ⑦ 面试会怎么问

问题0x69 && 0x55 的结果是什么?0x69 & 0x55 呢?

回答0x69 && 0x55 → 两个都是非零值,都视为 TRUE,TRUE && TRUE = TRUE,所以结果是 1(逻辑与)。0x69 & 0x5501101001 & 01010101 = 01000001 = 0x41(按位与)。同样的符号用在不同上下文中,含义完全不同。

3.4.8 ⑧ 一句话总结

!note

&/|/~/^ 是直接在二进制位上的手术刀,&&/||/! 是判断"整体是否为真"的开关。前者返回多位的值,后者只返回 0 或 1。

3.4.9 知识点 4 自测 🧠
3.4.9.1 基础题(5 题)
  1. 1 ^ 1 的结果是多少?
  2. 0x0F & 0xF0 的结果是多少?
  3. ~0 在 8 位系统上的结果是多少?
  4. 0x69 | 0x55 的结果是多少?
  5. a ^ a 对于任意 a 的结果是多少?
3.4.9.2 理解题(5 题)
  1. 为什么说 & 适合做"掩码"操作?举例说明。
  2. 用异或运算交换两个变量的值,原理是什么?
  3. 逻辑与 && 的"短路求值"是什么意思?为什么这很重要?
  4. 为什么位级运算比算术运算(加减乘除)更快?
  5. "位向量表示集合":用位向量 [01101001] 表示集合 {0, 3, 5, 6} 的原理是什么?
3.4.9.3 思考题(3 题)
  1. x & (x - 1) 这个表达式有什么特殊用途?
  2. 如何仅用位运算判断一个数是否是 2 的幂?
  3. 布尔代数中 &| 有分配律(a & (b | c) = (a & b) | (a & c)),但整数 *+ 也有分配律。为什么"布尔代数像整数代数"这件事很重要?
3.4.9.4 面试/表达题(5 题)
  1. 写一个宏 IS_POWER_OF_2(x),判断 x 是否是 2 的幂。
  2. 如何用位运算实现"将 x 的第 n 位设为 1"和"将 x 的第 n 位清零"?
  3. 用位向量表示 8 个信号,0x3C 表示哪些信号有效?
  4. 在什么场景下 a & ba && b 的结果相同?
  5. x ^ y ^ y 的结果是什么?解释原理和应用场景。

点击查看答案

基础题

  1. 0(异或:相同为 0)。
  2. 0x0000001111 & 11110000 = 00000000)。
  3. 0xFF00000000 每位取反 → 11111111)。
  4. 0x7D01101001 | 01010101 = 01111101)。
  5. 0(任何数和自己异或都等于 0)。

理解题

  1. 掩码操作:用一个位模式"筛选"出想要的位。例如 x & 0xFF 保留最低 8 位,其他位清零。0xFF 就是掩码------需要保留的位置放 1,需要清零的位置放 0。
  2. 三步:a = a ^ bb = a ^ b(此时 b 变成原来的 a),a = a ^ b(此时 a 变成原来的 b)。原理是 x ^ y ^ y = x,连续异或同一个值两次就"抵消"了。
  3. 如果 && 左边为假,右边根本不会执行。例如 p != NULL && *p > 0,如果 p 是空指针,*p 就不会执行,避免了空指针解引用崩溃。
  4. 位级运算在硬件上只需一层与/或/非门,一个时钟周期完成。加法需要进位链(多层门延迟),乘法需要多个加法器组合,除法更慢。
  5. 位向量的第 i 位(最低位是第 0 位)为 1,表示 i 属于集合。[01101001]:第 0、3、5、6 位为 1,所以集合是 {0, 3, 5, 6}。

思考题

  1. x & (x - 1) 会清除 x 最低位的 1。用于统计二进制中有多少个 1(Brian Kernighan 算法)、判断是否为 2 的幂。
  2. (x != 0) && ((x & (x - 1)) == 0)。如果是 2 的幂,二进制中只有一个 1,减 1 后那个 1 变 0,后面全变 1,按位与结果必为 0。
  3. 因为布尔代数和整数代数的结构相似,编译器可以利用同样的数学性质(交换律、结合律、分配律)对位运算表达式做优化重排。同时,这些性质让程序员可以用位运算替代某些算术运算。

面试/表达题

  1. #define IS_POWER_OF_2(x) ((x) != 0 && ((x) & ((x) - 1)) == 0)
  2. 设第 n 位为 1:x | (1 << n)。将第 n 位清零:x & ~(1 << n)。翻转第 n 位:x ^ (1 << n)
  3. 0x3C = 0011 1100,第 2、3、4、5 位为 1,表示信号 2、3、4、5 有效。
  4. 当 a 和 b 都只能取 0 或 1 时,按位与和逻辑与的结果相同(两者都返回 0 或 1 中的一个)。
  5. 结果是 x。原理:y ^ y = 0x ^ 0 = x。应用:加密(用密钥异或加密,再异或同一个密钥解密);RAID 奇偶校验;图形学中的异或绘制。

3.5 知识点 5:C 语言移位运算

3.5.1 ① 什么是它(一句话)

左移<<)将位模式整体左移,右边补 0。逻辑右移>>,无符号数)左边补 0。算术右移>>,有符号数)左边补符号位。移位量不能为负或超过字长。

3.5.2 ② 大白话解释

把二进制数字想象成一排人。左移就是让所有人同时向左走 k 步,最左边 k 个人掉出队列(丢弃),右边空出来的 k 个位置被"替补队员 0"补上。

右移有两种:

  • 逻辑右移:所有人向右走,左边空出来的位置全部补 0。无符号数用这种方式。
  • 算术右移:所有人向右走,左边空出来的位置补"原来的第一个人"(符号位)------如果原来是负数(最左边是 1),左边就补 1;如果是正数(最左边是 0),就补 0。这意味着负数右移后还是负数。
生活例子 对应关系 它帮你理解什么 类比的边界
排队的士兵 士兵 → 位,左移 → 全体向左走,掉出队列 → 丢弃高位 移位的"丢弃"和"补位" 士兵不会因为移动而改变值
十进制小数点移动 ×10 → 小数点右移一位(相当于十进制左移),÷10 → 小数点左移一位 左移 ≈ 乘以 2^k,右移 ≈ 除以 2^k 十进制是 10 的幂,二进制是 2 的幂
滚动字幕 LED 屏 字符整体向左滚动,右边出现空白 左移后低位补 0 LED 屏是循环的,移位通常不循环
3.5.3 ③ 为什么会有它
  • 如果没有移位运算 :乘/除以 2 的幂必须用作真正的乘除法指令------乘法指令可能比移位慢 3~10 倍(在旧机器上差距更大)。编译器大量使用移位来优化 x * 8(变成 x << 3)。
  • 算术右移的意义 :保持负数符号位不变,这样 -8 >> 1 得到 -4,而不是一个很大的正数。如果右移时补 0,-8(二进制 11111000)右移 1 位变成 01111100(正数 124),完全错误。
  • 为什么 C 标准不对有符号数右移做硬性规定:因为不同硬件实现可能不同。几乎所有现代机器都用算术右移,但标准为了兼容留了余地。
3.5.4 ④ 工作流程 / 演进过程

#mermaid-svg-znX5DGHK55r3XV3o{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-znX5DGHK55r3XV3o .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-znX5DGHK55r3XV3o .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-znX5DGHK55r3XV3o .error-icon{fill:#552222;}#mermaid-svg-znX5DGHK55r3XV3o .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-znX5DGHK55r3XV3o .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-znX5DGHK55r3XV3o .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-znX5DGHK55r3XV3o .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-znX5DGHK55r3XV3o .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-znX5DGHK55r3XV3o .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-znX5DGHK55r3XV3o .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-znX5DGHK55r3XV3o .marker{fill:#333333;stroke:#333333;}#mermaid-svg-znX5DGHK55r3XV3o .marker.cross{stroke:#333333;}#mermaid-svg-znX5DGHK55r3XV3o svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-znX5DGHK55r3XV3o p{margin:0;}#mermaid-svg-znX5DGHK55r3XV3o .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-znX5DGHK55r3XV3o .cluster-label text{fill:#333;}#mermaid-svg-znX5DGHK55r3XV3o .cluster-label span{color:#333;}#mermaid-svg-znX5DGHK55r3XV3o .cluster-label span p{background-color:transparent;}#mermaid-svg-znX5DGHK55r3XV3o .label text,#mermaid-svg-znX5DGHK55r3XV3o span{fill:#333;color:#333;}#mermaid-svg-znX5DGHK55r3XV3o .node rect,#mermaid-svg-znX5DGHK55r3XV3o .node circle,#mermaid-svg-znX5DGHK55r3XV3o .node ellipse,#mermaid-svg-znX5DGHK55r3XV3o .node polygon,#mermaid-svg-znX5DGHK55r3XV3o .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-znX5DGHK55r3XV3o .rough-node .label text,#mermaid-svg-znX5DGHK55r3XV3o .node .label text,#mermaid-svg-znX5DGHK55r3XV3o .image-shape .label,#mermaid-svg-znX5DGHK55r3XV3o .icon-shape .label{text-anchor:middle;}#mermaid-svg-znX5DGHK55r3XV3o .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-znX5DGHK55r3XV3o .rough-node .label,#mermaid-svg-znX5DGHK55r3XV3o .node .label,#mermaid-svg-znX5DGHK55r3XV3o .image-shape .label,#mermaid-svg-znX5DGHK55r3XV3o .icon-shape .label{text-align:center;}#mermaid-svg-znX5DGHK55r3XV3o .node.clickable{cursor:pointer;}#mermaid-svg-znX5DGHK55r3XV3o .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-znX5DGHK55r3XV3o .arrowheadPath{fill:#333333;}#mermaid-svg-znX5DGHK55r3XV3o .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-znX5DGHK55r3XV3o .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-znX5DGHK55r3XV3o .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-znX5DGHK55r3XV3o .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-znX5DGHK55r3XV3o .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-znX5DGHK55r3XV3o .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-znX5DGHK55r3XV3o .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-znX5DGHK55r3XV3o .cluster text{fill:#333;}#mermaid-svg-znX5DGHK55r3XV3o .cluster span{color:#333;}#mermaid-svg-znX5DGHK55r3XV3o div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-znX5DGHK55r3XV3o .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-znX5DGHK55r3XV3o rect.text{fill:none;stroke-width:0;}#mermaid-svg-znX5DGHK55r3XV3o .icon-shape,#mermaid-svg-znX5DGHK55r3XV3o .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-znX5DGHK55r3XV3o .icon-shape p,#mermaid-svg-znX5DGHK55r3XV3o .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-znX5DGHK55r3XV3o .icon-shape .label rect,#mermaid-svg-znX5DGHK55r3XV3o .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-znX5DGHK55r3XV3o .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-znX5DGHK55r3XV3o .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-znX5DGHK55r3XV3o :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 左移 <<
右移 >>
无符号数
有符号数
操作数 x 和移位量 k
移位方向?
整体左移k位

右边补k个0

高位丢弃
操作数类型?
逻辑右移

左边补0
算术右移

左边补符号位

!example 🎬 动画脑补

一列火车有 w 节车厢,每节装着一个 0 或 1。左移:所有车厢同时往前(高位方向)冲 k 格,最前面 k 节冲出了铁轨(丢弃),后面 k 节空位被"空车厢 0"补上。逻辑右移:所有车厢往后冲,前面空位用空车厢补上。算术右移:前后冲,但前面空位由车头(符号位)的"克隆车厢"来补------如果车头是 1,补的就是 1。

3.5.5 ⑤ 图片说明

建议插入的图片

  • 左移、逻辑右移、算术右移三者的对比图(同一个位向量,三种操作的差异)
  • x << k = x * 2^k 的直观演示:以十进制类比
3.5.6 ⑥ 容易混淆的概念
对比维度 逻辑右移 算术右移 最关键区别
是什么 右移后左边补 0 右移后左边补原来的符号位 补 0 vs 补符号位
适用类型 无符号数 有符号数(几乎所有的现代 C 编译器) 由数据类型决定
对负数的影响 负数变成很大的正数(错!) 负数保持为负,-8 >> 1 = -4 算术右移保持符号
C 语言保证 无符号数右移是逻辑右移(保证) 有符号数右移可能是算术也可能是逻辑(未保证,但实践上都是算术) 有符号右移不可移植
3.5.7 ⑦ 面试会怎么问

问题-1 >> 1 在大多现代机器上结果是多少?

回答-1 的补码表示(8 位)是 11111111,算术右移 1 位,左边补符号位 1,得到 11111111(仍然是 -1)。所以 -1 >> 1 = -1,不是 0。

3.5.8 ⑧ 一句话总结

!note

左移 = 乘以 2^k,右移 = 除以 2^k。无符号数用逻辑右移(补 0),有符号数用算术右移(补符号位)。移位一定要确保移位量在合法范围内。

3.5.9 知识点 5 自测 🧠
3.5.9.1 基础题(5 题)
  1. 0x8 << 2 的结果是多少?
  2. 0x80 >> 3(无符号)的结果是多少?
  3. 移位量 k 应该满足什么条件?
  4. 左移一位相当于乘以多少?
  5. 逻辑右移和算术右移的本质区别是什么?
3.5.9.2 理解题(5 题)
  1. 为什么 x << 1 有时会比 x * 2 更快?
  2. C 语言中 -16 >> 2 的值是多少?为什么?
  3. 如果 C 语言对 int 用逻辑右移而不是算术右移,-16 >> 2 的值会是多少(假设 8 位)?
  4. 为什么 C 语言标准不要求有符号数右移必须是算术右移?
  5. 1 << 31 在 32 位系统上得到什么值?它是有符号数的什么特殊值?
3.5.9.3 思考题(3 题)
  1. 移位操作能替代任意常数的乘法吗?例如 x * 7
  2. 如果移位量 k 等于或大于字长 w,会发生什么?(如 1 << 32 在 32 位系统上)
  3. 如何实现一个"循环左移"(rotating shift):左移出去的位补到右边?
3.5.9.4 面试/表达题(5 题)
  1. 解释左移、逻辑右移、算术右移的区别,各自适用什么情况。
  2. (unsigned) -1 >> 1 的值是多少?(int) ((unsigned) -1 >> 1) 呢?
  3. 什么是未定义行为(undefined behavior)?为什么移位操作中存在未定义行为?
  4. 用移位实现"求 x 的绝对值"(仅用位运算和加法)。
  5. x << 3 等价于 x * 8,这个关系在补码下是否始终成立?是否可能溢出?

点击查看答案

基础题

  1. 0x200x8=0b1000,左移 2 位 → 0b100000 = 0x20 = 32)。
  2. 0x100x80=128,除以 8=16=0x10)。
  3. 0 ≤ k < w(w 为字长),不能为负,不能等于或超过字长。
  4. 2。
  5. 逻辑右移左边补 0,算术右移左边补符号位(保持正/负性不变)。

理解题

  1. 左移指令通常 1 个时钟周期,整数乘法可能需要 3 个或更多时钟周期。编译器会自动将 x*2 优化为 x<<1,但了解这一点有助于理解编译器行为。
  2. -4-16 的 8 位补码是 11110000,算术右移 2 位 → 11111100 = -4
  3. 11110000 逻辑右移 2 位 → 00111100 = 60,完全错误。
  4. 为了兼容不同的硬件------有些早期机器可能只支持逻辑右移。不过几乎所有现代 CPU 都支持算术右移,主流编译器也都使用算术右移。
  5. 0x80000000,即 INT_MIN-2147483648)。在补码中,这是一个边界值,取负(-INT_MIN)会溢出。

思考题

  1. 可以,但需要组合。x*7 = (x<<3) - x = x*8 - x。编译器通常对于乘常数会尝试用移位和加减法组合替代。
  2. 在 C 语言中是未定义行为。在 x86 上,移位量会被截断(只取低 5 位),1<<32 等同于 1<<0 = 1。但绝不能依赖此行为。
  3. 循环左移:(x << k) | (x >> (w - k))。高位被左移推出去的位,同时从右移进来(通过右移 w-k 位放到低位)。

面试/表达题

  1. 左移:整体左移,右边补 0,等价乘 2^k。逻辑右移:右边移出,左边补 0,无符号数使用。算术右移:右边移出,左边补符号位,有符号数使用(保持正负性)。
  2. (unsigned) -1 = UINT_MAX(全 1),右移 1 位(逻辑右移)→ 最高位变 0,得到 0x7FFFFFFF(32 位下是 2147483647)。转回 int 后仍是正数 2147483647。
  3. 未定义行为:C 标准不规定结果的动作。移位操作中包括:移位量为负数、移位量 ≥ 字长。这意味着不同编译器/平台可能有不同结果,程序不可移植。
  4. int mask = x >> 31;(算术右移,正数得 0,负数得全 1);return (x + mask) ^ mask;(正数不变,负数 = ~x + 1 = -x)。
  5. 补码和左移的关系等价于乘法(位级行为一致)。但是,当结果超出表示范围时,乘法会溢出,左移也会有同样的溢出行为。实际上编译器利用这一等价性来优化,因为溢出行为在位级上是一致的。

3.6 知识点 6:无符号数与补码编码

3.6.1 ① 什么是它(一句话)

无符号数 (unsigned)将位模式直接解释为非负整数。补码 (two's-complement)是 C 语言 int 的编码方式,最高有效位解释为负权(-2^(w-1)),其他位仍是正权。

3.6.2 ② 大白话解释

假设你有 4 个位。无符号数的解读方式:每个位都代表一个正的 2 的幂。1001 = 1×8 + 0×4 + 0×2 + 1×1 = 9。

补码的解读方式:最高位是"负的" 2 的幂,其余位正常。还是 1001,但现在:

  • 最高位(最左边):负权 -1×8 = -8(因为它代表 -2^3
  • 第三位:0×4 = 0
  • 第二位:0×2 = 0
  • 最低位:1×1 = 1
  • 总和:-8 + 1 = -7

同一个位模式 1001,无符号解读是 9,补码解读是 -7。位模式一模一样,只是"解读规则"不同

位模式(4位) 无符号值 补码值
0000 0 0
0001 1 1
0111 7 7
1000 8 -8
1111 15 -1

补码的精妙之处:负数范围比正数多 1(4 位:-8 到 7),而且加法硬件完全不需要知道你在做有符号还是无符号------同一套电路对两种情况都正确。

生活例子 对应关系 它帮你理解什么 类比的边界
钟表 12 点=0 点。13 点=1 点(模 12)。补码的负数就像"逆时针转"。 模运算:转一圈回到原点 钟表模 12,补码模 2^w
里程表(汽车) 全 9 再转一格就变全 0。1111 加 1 变 0000(溢出) 溢出就像里程表"转回去" 里程表是十进制不是二进制
借钱和还钱(负债务) 你有 0 元,借了 1 元 → 账面是"负 1",可以通过还 1 元清零。补码的 -1 就是 1111(全 1),加 1 变成 0000 补码的减法可以变成加法 债务有法律含义,补码只是约定
3.6.3 ③ 为什么会有它
  • 如果没有补码 :表示负数需要额外的符号位和处理逻辑。反码(Ones' Complement)和原码(Sign-Magnitude)都有"+0 和 -0 两种零"的怪异问题。补码只有一个零(0000),而且硬件可以用同一套加法器做有符号和无符号运算。
  • 为什么负数范围比正数多 1 :因为 0 是"非负的",占了一个正半轴的位置。w 位补码的范围是 [-2^(w-1), 2^(w-1)-1],不对称。
  • 为什么几乎所有现代机器都用补码:因为硬件实现最简单,加法和减法的电路无需区分有无符号。
3.6.4 ④ 工作流程 / 演进过程

#mermaid-svg-tKTMvZRKEG0QPC1Y{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tKTMvZRKEG0QPC1Y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tKTMvZRKEG0QPC1Y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tKTMvZRKEG0QPC1Y .error-icon{fill:#552222;}#mermaid-svg-tKTMvZRKEG0QPC1Y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tKTMvZRKEG0QPC1Y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tKTMvZRKEG0QPC1Y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tKTMvZRKEG0QPC1Y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tKTMvZRKEG0QPC1Y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tKTMvZRKEG0QPC1Y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tKTMvZRKEG0QPC1Y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tKTMvZRKEG0QPC1Y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tKTMvZRKEG0QPC1Y .marker.cross{stroke:#333333;}#mermaid-svg-tKTMvZRKEG0QPC1Y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tKTMvZRKEG0QPC1Y p{margin:0;}#mermaid-svg-tKTMvZRKEG0QPC1Y .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tKTMvZRKEG0QPC1Y .cluster-label text{fill:#333;}#mermaid-svg-tKTMvZRKEG0QPC1Y .cluster-label span{color:#333;}#mermaid-svg-tKTMvZRKEG0QPC1Y .cluster-label span p{background-color:transparent;}#mermaid-svg-tKTMvZRKEG0QPC1Y .label text,#mermaid-svg-tKTMvZRKEG0QPC1Y span{fill:#333;color:#333;}#mermaid-svg-tKTMvZRKEG0QPC1Y .node rect,#mermaid-svg-tKTMvZRKEG0QPC1Y .node circle,#mermaid-svg-tKTMvZRKEG0QPC1Y .node ellipse,#mermaid-svg-tKTMvZRKEG0QPC1Y .node polygon,#mermaid-svg-tKTMvZRKEG0QPC1Y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tKTMvZRKEG0QPC1Y .rough-node .label text,#mermaid-svg-tKTMvZRKEG0QPC1Y .node .label text,#mermaid-svg-tKTMvZRKEG0QPC1Y .image-shape .label,#mermaid-svg-tKTMvZRKEG0QPC1Y .icon-shape .label{text-anchor:middle;}#mermaid-svg-tKTMvZRKEG0QPC1Y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tKTMvZRKEG0QPC1Y .rough-node .label,#mermaid-svg-tKTMvZRKEG0QPC1Y .node .label,#mermaid-svg-tKTMvZRKEG0QPC1Y .image-shape .label,#mermaid-svg-tKTMvZRKEG0QPC1Y .icon-shape .label{text-align:center;}#mermaid-svg-tKTMvZRKEG0QPC1Y .node.clickable{cursor:pointer;}#mermaid-svg-tKTMvZRKEG0QPC1Y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tKTMvZRKEG0QPC1Y .arrowheadPath{fill:#333333;}#mermaid-svg-tKTMvZRKEG0QPC1Y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tKTMvZRKEG0QPC1Y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tKTMvZRKEG0QPC1Y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tKTMvZRKEG0QPC1Y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tKTMvZRKEG0QPC1Y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tKTMvZRKEG0QPC1Y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tKTMvZRKEG0QPC1Y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tKTMvZRKEG0QPC1Y .cluster text{fill:#333;}#mermaid-svg-tKTMvZRKEG0QPC1Y .cluster span{color:#333;}#mermaid-svg-tKTMvZRKEG0QPC1Y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tKTMvZRKEG0QPC1Y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tKTMvZRKEG0QPC1Y rect.text{fill:none;stroke-width:0;}#mermaid-svg-tKTMvZRKEG0QPC1Y .icon-shape,#mermaid-svg-tKTMvZRKEG0QPC1Y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tKTMvZRKEG0QPC1Y .icon-shape p,#mermaid-svg-tKTMvZRKEG0QPC1Y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tKTMvZRKEG0QPC1Y .icon-shape .label rect,#mermaid-svg-tKTMvZRKEG0QPC1Y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tKTMvZRKEG0QPC1Y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tKTMvZRKEG0QPC1Y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tKTMvZRKEG0QPC1Y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 位模式 1100
无符号解释
补码解释
8 + 4 + 0 + 0 = 12
最高位-1×8=-8

  • 1×4 + 0 + 0 = -4
3.6.5 ⑤ 图片说明

建议插入的图片

  • 无符号数和补码的"权"对比图:同样 4 位,无符号每位都是正权,补码最高位是负权
  • w=4 时所有 16 个位模式对应的无符号值和补码值对照表
  • "补码轮"示意图:0 在正上方,顺时针加 1,逆时针减 1
3.6.6 ⑥ 容易混淆的概念
对比维度 无符号数 补码 最关键区别
是什么 每位都是正权,只能表示非负数 最高位是负权,能表示正/负数和零 最高位的"权重符号"
范围(w=32) 0, 4294967295 -2147483648, 2147483647 范围形状完全不同
0 的表示 只有 000...0 只有 000...0(一个零) 补码只有一个零(优势)
C 语言对应 unsigned int int(默认) unsigned 关键字区分
3.6.7 ⑦ 面试会怎么问

问题int 类型的 TMin(最小负数)和 TMax(最大正数)分别是多少(32 位)?为什么 TMin 的绝对值比 TMax 的绝对值大 1?

回答TMin = -2^31 = -2147483648TMax = 2^31 - 1 = 2147483647。不对称是因为 0 在补码中占了一个"非负"的位置(000...0),所以正数的最大值比负数的最负值少 1。这也是为什么 -INT_MIN 会溢出------INT_MIN 没有对应的正数。

3.6.8 ⑧ 一句话总结

!note

补码把最高位的 2^(w-1) 从正权改为负权,使得同一个位模式既能表示整数又能表示负数。关键是:位模式不变,解释规则决定数值

3.6.9 知识点 6 自测 🧠
3.6.9.1 基础题(5 题)
  1. 4 位无符号数的最大值和最小值分别是多少?
  2. 4 位补码的范围是多少(最小到最大)?
  3. 位模式 1111(4 位)分别用无符号和补码解释,数值各是多少?
  4. 补码中 -1 的位模式是什么(假设 4 位)?
  5. 补码的全称(英文)是什么?
3.6.9.2 理解题(5 题)
  1. 为什么补码只有一个"零"的表示,而反码有两个?
  2. 为什么说同一套硬件电路可以同时做无符号和补码加法?
  3. 为什么 int TMin 的绝对值比 TMax 大 1?
  4. B2U_4([1000]) 的值是多少?B2T_4([1000]) 的值是多少?
  5. 为什么补码的加法硬件可以跟无符号数共用?
3.6.9.3 思考题(3 题)
  1. 是否存在一个 C 表达式,其值同时等于 INT_MAXUINT_MAX
  2. 设计一个"只有无符号数"的编程语言有什么优缺点?
  3. 如果让你设计一种新的整数编码,你会从哪里改进补码?
3.6.9.4 面试/表达题(5 题)
  1. 用公式写出无符号数编码函数 B2U_w(x) 和补码编码函数 B2T_w(x)
  2. (unsigned) -1 的值是什么?这个转换发生了什么?
  3. 解释"补码"名字的由来:为什么叫"two's complement"?
  4. Java 为什么不支持无符号整数类型?
  5. 如何求一个补码负数的绝对值?有什么陷阱?

点击查看答案

基础题

  1. 无符号:0 到 15(2^4 - 1)。补码:-8 到 7(-2^3 到 2^3 - 1)。
  2. 1111 无符号值=15,补码值=-1。
  3. 补码:-1 的 4 位表示是 1111
  4. B2U_4([1000]) = 8B2T_4([1000]) = -8
  5. Two's-complement。

理解题

  1. 补码中,最高位的权是 -2^(w-1),其余位正权。对于零:000...0,所有位为 0,值为 0。如果全 1:111...1 = -2^(w-1) + (2^(w-1)-1) = -1,不是 0。反码中全 0 = +0,全 1 = -0,有两个零。
  2. 因为补码和无符号数加法在位级上完全一致------都是逐位相加,进位传播,溢出截断。唯一的区别在于"你怎么看这个结果"。
  3. w 位补码范围是 [-2^(w-1), 2^(w-1)-1]。0 占了非负数区域的一个位置(000...0),所以正数最大值少 1。|-2^(w-1)| = 2^(w-1),超出正数最大值 2^(w-1)-1
  4. B2U_4([1000]) = 8B2T_4([1000]) = -8。同一个位模式,解释不同。
  5. 补码加法电路只做二进制加法+忽略最高位进位(截断),结果的位模式对两种解释都是正确的。计算时不需要知道数字是有符号还是无符号。

思考题

  1. C 语言中每个类型的范围是确定的,INT_MAX = 2^31 - 1UINT_MAX = 2^32 - 1,没有值能同时等于两者。但同一个位模式(如 0xFFFFFFFF)被解释为无符号是 UINT_MAX,解释为补码是 -1
  2. 优点:没有隐式类型转换的陷阱,行为更可预测。缺点:无法表示负数,很多算法(如差值、偏移)需要负数。
  3. 可考虑用额外的"溢出标记位"替代截断、用不同的偏置(如 BCD 编码)、或在硬件层面支持可选的高精度扩展。但补码的"硬件简单性"优势很难超越。

面试/表达题

  1. B2U_w(x) = Σ(x_i × 2^i) i=0...w-1;B2T_w(x) = -x_(w-1) × 2^(w-1) + Σ(x_i × 2^i) i=0...w-2。
  2. (unsigned) -1 = UINT_MAX。转换过程中位模式不变(-1 的补码是全 1),改变的是解释方式------从补码全 1(= -1)变为无符号全 1(= UINT_MAX)。这是 C 语言转换规则"位模式不变,数值可能改变"的经典例子。
  3. "two's complement":对于非负数 x,-x 的 w 位补码表示通过计算 x' = 2^w - x 得到------2^w 这个"2 的幂"参与取补,所以叫"two's"。反码("ones' complement")则是用 (2^w - 1) - x 计算,参与的是全 1 的模式。
  4. Java 的设计哲学是简洁和安全,他们认为无符号数带来的混淆(如 C 中的隐式转换陷阱)超过其收益。但 Java 8 之后也提供了 Integer.toUnsignedString() 等方法处理无符号语义。
  5. 绝对值:正数不变,负数取 -x = ~x + 1。陷阱:INT_MIN-2147483648)取绝对值会溢出-------INT_MIN = INT_MIN,因为 INT_MIN 在补码中没有对应的正数。

3.7 知识点 7:有符号数与无符号数的转换------隐式类型陷阱

3.7.1 ① 什么是它(一句话)

C 语言中,有符号数和无符号数之间的强制类型转换 遵循"位模式不变,数值可能改变 "的原则。当两者混用在同一个表达式中时,有符号数会被隐式转换为无符号数,这是无数 bug 的根源。

3.7.2 ② 大白话解释

想象两种"语言"解读同一个手势。手势本身(位模式)没变,但不同语言里这个手势的含义完全不一样。

C 语言的转换规则:T2U(x) = 如果 x<0 则 x+2^w,否则 x。U2T(x) = 如果 x>TMax_w 则 x-2^w,否则 x。

最经典的陷阱:-1 < 0U。因为 -1 是有符号数,0U 是无符号数,表达式中有无符号数时,有符号数被隐式转换为无符号数。(unsigned) -1 = UINT_MAX(约 43 亿),而 0U 是 0。所以 4294967295U < 0U 为假!这个表达式居然是 false

生活例子 对应关系 它帮你理解什么 类比的边界
外国人看本地手势 同一个手势 → 同一个位模式;不同文化 → 不同解释 转换不改变位,改变意义 手势在不同文化中有不同含义,但至少都"有意义"
温度计刻度 同一位置 → 同一读数;摄氏度 → 无符号(只能非负);华氏度 → 有符号(可能为负) 相同刻度,不同解释 实际有绝对零度限制
Excel 单元格格式 同一个数字,显示为"文本"vs"数字"vs"日期" 底层值不变,显示/解读方式不同 Excel 可能有四舍五入
3.7.3 ③ 为什么会有它
  • 为什么不禁止混用:历史原因------C 语言从时代起就允许这种灵活性。禁止混用会破坏大量现有代码。
  • 为什么隐式转换方向是"往无符号转":这是 C 语言标准的"通常算术转换"规则------当无符号和有符号类型同时出现时,两者都转为无符号。这个规则不是任何人主观设计的好方案,而是历史演进的结果。
3.7.4 ④ 关键转换公式

#mermaid-svg-g7QGwOjTAhCnH17y{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-g7QGwOjTAhCnH17y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-g7QGwOjTAhCnH17y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-g7QGwOjTAhCnH17y .error-icon{fill:#552222;}#mermaid-svg-g7QGwOjTAhCnH17y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-g7QGwOjTAhCnH17y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-g7QGwOjTAhCnH17y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-g7QGwOjTAhCnH17y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-g7QGwOjTAhCnH17y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-g7QGwOjTAhCnH17y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-g7QGwOjTAhCnH17y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-g7QGwOjTAhCnH17y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-g7QGwOjTAhCnH17y .marker.cross{stroke:#333333;}#mermaid-svg-g7QGwOjTAhCnH17y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-g7QGwOjTAhCnH17y p{margin:0;}#mermaid-svg-g7QGwOjTAhCnH17y .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-g7QGwOjTAhCnH17y .cluster-label text{fill:#333;}#mermaid-svg-g7QGwOjTAhCnH17y .cluster-label span{color:#333;}#mermaid-svg-g7QGwOjTAhCnH17y .cluster-label span p{background-color:transparent;}#mermaid-svg-g7QGwOjTAhCnH17y .label text,#mermaid-svg-g7QGwOjTAhCnH17y span{fill:#333;color:#333;}#mermaid-svg-g7QGwOjTAhCnH17y .node rect,#mermaid-svg-g7QGwOjTAhCnH17y .node circle,#mermaid-svg-g7QGwOjTAhCnH17y .node ellipse,#mermaid-svg-g7QGwOjTAhCnH17y .node polygon,#mermaid-svg-g7QGwOjTAhCnH17y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-g7QGwOjTAhCnH17y .rough-node .label text,#mermaid-svg-g7QGwOjTAhCnH17y .node .label text,#mermaid-svg-g7QGwOjTAhCnH17y .image-shape .label,#mermaid-svg-g7QGwOjTAhCnH17y .icon-shape .label{text-anchor:middle;}#mermaid-svg-g7QGwOjTAhCnH17y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-g7QGwOjTAhCnH17y .rough-node .label,#mermaid-svg-g7QGwOjTAhCnH17y .node .label,#mermaid-svg-g7QGwOjTAhCnH17y .image-shape .label,#mermaid-svg-g7QGwOjTAhCnH17y .icon-shape .label{text-align:center;}#mermaid-svg-g7QGwOjTAhCnH17y .node.clickable{cursor:pointer;}#mermaid-svg-g7QGwOjTAhCnH17y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-g7QGwOjTAhCnH17y .arrowheadPath{fill:#333333;}#mermaid-svg-g7QGwOjTAhCnH17y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-g7QGwOjTAhCnH17y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-g7QGwOjTAhCnH17y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-g7QGwOjTAhCnH17y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-g7QGwOjTAhCnH17y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-g7QGwOjTAhCnH17y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-g7QGwOjTAhCnH17y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-g7QGwOjTAhCnH17y .cluster text{fill:#333;}#mermaid-svg-g7QGwOjTAhCnH17y .cluster span{color:#333;}#mermaid-svg-g7QGwOjTAhCnH17y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-g7QGwOjTAhCnH17y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-g7QGwOjTAhCnH17y rect.text{fill:none;stroke-width:0;}#mermaid-svg-g7QGwOjTAhCnH17y .icon-shape,#mermaid-svg-g7QGwOjTAhCnH17y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-g7QGwOjTAhCnH17y .icon-shape p,#mermaid-svg-g7QGwOjTAhCnH17y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-g7QGwOjTAhCnH17y .icon-shape .label rect,#mermaid-svg-g7QGwOjTAhCnH17y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-g7QGwOjTAhCnH17y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-g7QGwOjTAhCnH17y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-g7QGwOjTAhCnH17y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

有符号数 x
x < 0?
x + 2^w → 无符号数
x(不变)→ 无符号数

3.7.5 ⑤ 图片说明

建议插入的图片

  • T2U 和 U2T 的"映射图":一个圆圈上标注补码值和无符号值的对应关系
  • C 语言表达式 -1 < 0U 的逐步求值过程图
3.7.6 ⑥ 容易混淆的概念
对比维度 显式转换 (unsigned) x 隐式转换(表达式混用) 最关键区别
是什么 程序员主动写的强制类型转换 编译器自动插入的转换 显式是主动的,隐式是"潜规则"
何时发生 代码中写了 (unsigned) 表达式中有无符号数和有符号数混用 隐式转换可能让新手完全不知情
风险 程序员知道自己在做什么 行为难以预料,容易引入 bug 隐式转换是"暗箭"
3.7.7 ⑦ 面试会怎么问

问题sizeof 返回的是无符号类型 size_t。解释为什么 sizeof(s) < strlen(s) - 1 可能导致死循环。

回答sizeof 返回 size_t(无符号),strlen 也返回 size_t。如果 s 是空字符串,strlen(s) = 0strlen(s) - 1 = (size_t) -1 = SIZE_MAX(无符号下溢),这是一个巨大的正数。sizeof(s) 不可能大于 SIZE_MAX,所以比较为真,可能导致缓冲区越界或死循环。正确做法:先将长度转为有符号比较,或改用条件判断。

3.7.8 ⑧ 一句话总结

!note

有符号和无符号混用时,有符号会被隐式转为无符号。-1 < 0U 居然是假。避免混用,或者用 gcc -Wsign-conversion 让编译器帮你检查。

3.7.9 知识点 7 自测 🧠
3.7.9.1 基础题(5 题)
  1. (unsigned) -1 的值是什么(假设 32 位)?
  2. T2U_4(-8) 的值是多少?
  3. U2T_4(15) 的值是多少?
  4. C 语言中,intunsigned 混用时,哪个会隐式转换?
  5. size_t 是有符号还是无符号类型?
3.7.9.2 理解题(5 题)
  1. 为什么 C 语言设计者选择"往无符号方向"隐式转换?
  2. T2U_w(x) = x + 2^w (当 x < 0),为什么这样是对的?用二进制位模式解释。
  3. 为什么 strlen(s) - 1 对于空字符串是一个危险表达式?
  4. Java 为什么不支持无符号整数?
  5. 补码和无符号数为什么可以进行相同的乘加运算?原理是什么?
3.7.9.3 思考题(3 题)
  1. 如果 C 语言不允许有符号和无符号隐式混用,会怎样?
  2. 如何检查一个无符号数转为有符号数是否会溢出?
  3. 设计一个安全的 subtract(a, b) 函数,返回 a - b,但不依赖有符号/无符号的隐式转换。
3.7.9.4 面试/表达题(5 题)
  1. 写出 T2U_w(x)U2T_w(u) 的公式并解释。
  2. 解释"C 语言的通常算术转换(usual arithmetic conversions)"。
  3. 在 32 位系统上,-2147483648 < 2147483647U 的结果是什么?为什么?
  4. 给出一个实际代码示例,其中有符号/无符号的隐式转换导致了 bug。
  5. 如何用位运算实现"检测无符号加法是否溢出"?

点击查看答案

基础题

  1. (unsigned) -1 = UINT_MAX = 4294967295(32 位)。
  2. T2U_4(-8) = -8 + 16 = 8(因为 -8 的位模式 1000 无符号解释就是 8)。
  3. U2T_4(15) = 15 - 16 = -1(因为 15 的位模式 1111 补码解释是 -1)。
  4. 有符号数被隐式转换为无符号数。
  5. 无符号类型。

理解题

  1. 不是主动的"设计选择",而是 C 标准中"通常算术转换"规则的历史结果:当一个 unsigned int 和一个 int 在一起时,int 被转为 unsigned int
  2. 因为 -x 的补码位模式(4 位)等于 2^w - x 的二进制。例如 -1 的位模式是 1111 = 15 = 16 - 1 = 2^4 - 1。所以 T2U:当 x<0 时,位模式 = 2^w + x(x 为负,2^w + x 为正)。
  3. strlen(s) 返回 size_t(无符号)。空字符串 strlen(s)=00-1 在无符号下溢出变成 SIZE_MAX,如果将其作为循环计数或缓冲区大小,后果灾难性。
  4. Java 设计者认为无符号数带来的混淆超过其收益,且 Java 的目标是"简单和可预测",避免 C 中的这类陷阱。
  5. 因为位模式在两种解释下进行二进制加法得到完全相同的结果------有符号和无符号的加法在位级上是同一套操作(二进制加法 + 截断到 w 位)。这是补码设计最巧妙的地方。

思考题

  1. 好处:大量隐式转换 bug 消失。代价:大量遗留代码需要显式转换,迁移成本极高。Rust 实际上在这方面做了更严格的控制。
  2. 如果 u > INT_MAX(对于 w 位),则 U2T(u) = u - 2^w 会得到一个负数。检查方法:if (u > (unsigned) INT_MAX) { /* 会发生符号变化 */ }
  3. 返回类型为 int,先判断两个操作数的大小关系避免无符号溢出:当 b 是正数且大于 INT_MAX + a 时会溢出;当 b 是负数且小于 INT_MIN + a 时也会溢出。

面试/表达题

  1. T2U_w(x) = x + 2^w (x < 0)T2U_w(x) = x (x ≥ 0)U2T_w(u) = u - 2^w (u > TMax_w)U2T_w(u) = u (u ≤ TMax_w)。位模式保持不变,只是解释函数不同。
  2. 当算术运算的两个操作数类型不同时,C 执行隐式类型提升:如果任一操作数是 unsigned,两者都转换为 unsigned;否则转为更宽的类型(如 int→long→long long)。
  3. INT_MIN = -2147483648,跟无符号数 2147483647U 比较时,INT_MIN 被转为无符号:(unsigned) (-2147483648) = 2147483648U2147483648U < 2147483647U = 。又是 -1 < 0U 的翻版。
  4. XDR 安全漏洞(旁注):ele_cnt * ele_sizesize_t(无符号),特定的参数组合会导致乘法溢出,只分配 4096 字节而非 4GB,导致缓冲区溢出。影响了 Sun RPC、Internet Explorer、Kerberos 等大量软件。
  5. 参考 2.3.1 节:s = x + y(无符号),当 s < x(或 s < y)时发生溢出------因为正常加法 s≥x,溢出时 s=x+y-2^w<x(因为 y<2^w,所以 y-2^w<0)。

3.8 知识点 8:整数运算------加法溢出与乘法

3.8.1 ① 什么是它(一句话)

无符号加法 溢出的效果是"模 2^w"(结果 = 真实和 mod 2^w)。补码加法 溢出有两种:正溢出(两个正数相加得到负数)和负溢出(两个负数相加得到正数)。乘法比加法更容易溢出,但编译器会用移位和加减法组合来优化乘常数。

3.8.2 ② 大白话解释

溢出的故事:你有一个 4 位的电子计数器,显示范围 00001111(015)。

  • 无符号:9 + 12 = 21,但 21 = 10101(5 位),计数器只能显示 4 位,截掉最高位的 1,显示 0101 = 5。9 + 12 = 5?这就是"模 16"的效果(21 mod 16 = 5)。
  • 补码:9 + 7 = 16,但 16 无法用 4 位补码表示(最大是 7),结果是 1000 = -8。9 + 7 = -8 ?这是正溢出。反过来,-8 + (-5) = -13,但最小是 -8,截断后是 0011 = 3。-8 + (-5) = 3?这是负溢出。

检测溢出的技巧:

  • 无符号:s = x + y,如果 s < x 就溢出了。
  • 补码:两个正数相加得负 → 正溢出;两个负数相加得正 → 负溢出。
生活例子 对应关系 它帮你理解什么 类比的边界
汽车里程表 里程表到 999999 再走 1 公里变成 000000 溢出 = 转回起点(模运算) 里程表是十进制,计算机是二进制
24 小时钟 23 点 + 5 小时 = 4 点(模 24) 模加法形成阿贝尔群 时间溢出没有"符号"概念
银行账户限额 存款 90 亿 + 存款 50 亿 = 140 亿(但 32 位只能存 43 亿) → "余额变成负数" 大数据运算必须注意类型宽度 银行不会截断,会拒绝交易
3.8.3 ③ 为什么会有它
  • 为什么允许溢出而不是报错:硬件层面上,截断高位比检测溢出再发信号简单得多。大多数 CPU 确实会设置溢出标志位(如 x86 的 OF),但 C 语言不会自动检查。
  • 溢出检测的重要性 :XDR 安全漏洞就是因为 ele_cnt * ele_size 溢出导致只分配了 4096 字节而非 4GB,攻击者可以越界写入。Ariane 5 火箭爆炸是因为 64 位浮点转 16 位整数溢出。
3.8.4 ④ 关键运算规则

渲染错误: Mermaid 渲染失败: Parse error on line 3: ... B -->|无符号| C[结果 = (a + b) mod 2^w
-----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'

3.8.5 ⑤ 图片说明

建议插入的图片

  • 无符号加法的"山坡图":x 轴、y 轴取 0~15,z 轴是 x+y,正常区域和溢出区域用不同颜色
  • 补码加法坐标图:4 位下,x+y 在平面上形成的菱形区域,正溢出和负溢出区域高亮
3.8.6 ⑥ 容易混淆的概念
对比维度 无符号溢出 补码正溢出 补码负溢出
条件 x + y ≥ 2^w 正 + 正 = 负 负 + 负 = 正
结果 真实和 - 2^w 真实和 - 2^w 真实和 + 2^w
检测 sum < x x>0 && y>0 && sum<0 x<0 && y<0 && sum>0
3.8.7 ⑦ 面试会怎么问

问题 :如何检测两个 int 相加是否溢出?

回答 :分别判断正溢出和负溢出的条件。如果 a > 0 && b > 0 && a + b < 0,则正溢出;如果 a < 0 && b < 0 && a + b >= 0,则负溢出。但注意:有符号溢出在 C 中是未定义行为,所以这种检测本身在标准 C 中不可靠------应该转为无符号数运算后再判断。

3.8.8 ⑧ 一句话总结

!note

溢出不会报错,只会"默默地"截断。无符号溢出是模 2^w 的效果;补码溢出会让符号"翻脸"。编译器利用移位+加减法优化乘除,这些优化在位级上与溢出行为一致。

3.8.9 知识点 8 自测 🧠
3.8.9.1 基础题(5 题)
  1. 4 位无符号加法:9 + 12 的结果是多少?
  2. 4 位补码加法:5 + 5 的结果是多少?
  3. 4 位补码加法:-8 + (-5) 的结果是多少?
  4. 无符号加法溢出怎么检测?
  5. 乘以 2^k 等价于什么位运算?
3.8.9.2 理解题(5 题)
  1. 无符号加法是否满足交换律和结合律?
  2. 为什么 x - y < 0x < y 在补码下可能不等价?
  3. 为什么编译器把 x * 14 优化为 (x<<4) - (x<<1) 是正确的?
  4. 补码除法 x / 2^k 为什么不能直接用 x >> k
  5. 为什么无符号和补码的乘法在位级上是相同的?
3.8.9.3 思考题(3 题)
  1. 如果 CPU 提供了"溢出标志位",C 语言为什么不直接利用它来抛异常?
  2. 设计一个安全乘法 safe_multiply(a, b, *overflow) 来检测乘法溢出。
  3. Rust 语言如何处理整数溢出?与 C 的处理方式对比。
3.8.9.4 面试/表达题(5 题)
  1. 用 C 代码写出无符号加法的溢出检测函数 uadd_ok
  2. 用 C 代码写出补码加法的溢出检测函数 tadd_ok
  3. 为什么大多数 64 位系统的 int 还是 32 位?
  4. 为什么编译器不总是用移位优化乘法?
  5. 解释"补码的非":为什么 -x = ~x + 1?这对于所有 x 都成立吗?

点击查看答案

基础题

  1. 21 mod 16 = 5。
  2. 10 超出了 7,截断后 1010 的高位是 1 → -8+2 = -6(或:10-16 = -6)。
  3. -13 + 16 = 3。
  4. s = x + y;if (s < x) { /* overflow */ }(因为 s = x+y-2^w < x+y-y = x)。
  5. 左移:x << k

理解题

  1. 无符号加法满足交换律和结合律------它形成阿贝尔群(模 2^w 加法)。
  2. 因为有补码溢出的情况。例如 INT_MIN - 1 会溢出为一个正数,导致 x - y < 0x < y 结果不一致。编译器在优化时必须谨慎处理这种边界情况。
  3. 14 = 16 - 2 = 2^4 - 2^1x*16 = x<<4x*2 = x<<1,所以 x*14 = (x<<4) - (x<<1)。即使溢出,移位+减法的位级结果与乘法一致------这是由整数算术的代数属性保证的。
  4. 补码除法向零舍入(正数向下、负数向上),而算术右移总是向下舍入。对于负数(如 -7/2 = -3,但 -7>>1 = -4),直接移位的方向不对。需加偏置:(x + (1<<k) - 1) >> k
  5. 二进制乘法对位模式的操作是相同的------不论是 0110 × 0101 还是 -2 × 5,位级乘法+截断的结果完全一致。这就是补码设计最优雅的地方。

思考题

  1. 因为 C 的设计理念是"信任程序员"和高性能------自动检查溢出会降低每次算术运算的速度。其他语言(如 Ada、Rust Debug 模式)会选择不同的权衡。
  2. 判断:如果 a > 0 && b > 0 && a > INT_MAX / b → 正溢出;如果 a > 0 && b < 0 && b < INT_MIN / a → 负溢出,等等。转为无符号乘法或使用 __builtin_mul_overflow()
  3. Rust Debug 模式下整数溢出会 panic(类似断言失败);Release 模式下默认是 wrapping(模 2^w 的截断)。还提供显式方法:wrapping_add()checked_add()(返回 Option)、saturating_add()(饱和加法)。

面试/表达题

  1. int uadd_ok(unsigned x, unsigned y) { return x + y >= x; }
  2. int tadd_ok(int x, int y) { int sum = x + y; int neg_overflow = x < 0 && y < 0 && sum >= 0; int pos_overflow = x > 0 && y > 0 && sum < 0; return !neg_overflow && !pos_overflow; }。但要注意,补码溢出在 C 中是未定义行为,理论上这段代码不可靠。更安全的方法是将操作数转为无符号再判断。
  3. 历史兼容性------几十年的代码假设 int 是 32 位。改为 64 位会破坏大量程序。如果要用 64 位整数,用 long longint64_t
  4. 当移位和加减法的组合数量超过乘法指令的相对成本时,直接用乘法指令更快。例如 x * 3(x<<1) + x(两个指令)可能不如一条乘法指令。
  5. ~x + 1 = (2^w - 1 - x) + 1 = 2^w - x,而 -x 在补码下就是 2^w - x(模 2^w 下的逆元)。不成立的例外:INT_MIN(如 32 位下 -2147483648),-INT_MIN = INT_MIN(溢出,没有对应的正数)。

3.9 知识点 9:浮点数------IEEE 754 格式

3.9.1 ① 什么是它(一句话)

IEEE 754 是浮点数的工业标准,用 V = (-1)^s × M × 2^E 的形式编码实数。float(32 位)和 double(64 位)分别对应单精度和双精度浮点。

3.9.2 ② 大白话解释

IEEE 754 把一个浮点数分成三个部分:

  • 符号位 s(1 位):0→正数,1→负数。
  • 阶码 E(8 位 float / 11 位 double):表示 2 的多少次幂。范围大约是 10^(-38) 到 10^(38)。
  • 尾数 M(23 位 float / 52 位 double):表示"精度",即数字的有效位数。

三种情况:

  1. 规格化数(最常见):E = e - Bias,M = 1 + f(隐含一个 1 在前面)。
  2. 非规格化数(极小的数):E = 1 - Bias,M = f(没有隐含的 1)。用来"逐渐下溢"到 0。
  3. 特殊值 :指数全 1,尾数全 0 → ±∞;指数全 1,尾数非零 → NaN(如 sqrt(-1)0/0)。
生活例子 对应关系 它帮你理解什么 类比的边界
科学记数法 3.14×10^8 → (-1)^0 × 3.14 × 10^8。浮点就是二进制的科学记数法 规格化数 = "规范化"的科学记数 科学记数法以 10 为底,浮点以 2 为底
游标卡尺 尾数位数 → 卡尺精度(能多细);阶码范围 → 卡尺长度(能多长) 精度和范围是独立的两个维度 卡尺是线性的,浮点间距不均匀(接近 0 更密)
GPS 坐标 经纬度可以表示为"度.分.秒",浮点也一样------精度有限,只能逼近真实位置 浮点只是近似,不是精确值 GPS 定位还有信号误差
3.9.3 ③ 为什么会有它
  • 如果没有 IEEE 754 标准:1980 年代前,每个厂商有自己的浮点格式------IBM、DEC、Intel 各不相同。程序从一个机器移到另一个机器,浮点结果可能完全不同。IEEE 754 统一了格式,大幅提高可移植性。
  • 为什么浮点不能精确表示 0.1 :因为 0.1 = 1/10,而浮点以 2 为底。1/10 在二进制中是无限循环小数(0.0001100110011...),有限的尾数位数只能截断,产生近似值。就像 1/3 = 0.3333... 在十进制下无法精确表示。
3.9.4 ④ IEEE 754 编码流程

渲染错误: Mermaid 渲染失败: Parse error on line 6: ...值] C --> F按位拼接: s\|exp\|frac D --> F ----------------------^ Expecting 'SQE', 'TAGEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PIPE'

float 格式布局:|s(1)|exp(8)|frac(23)|,Bias=127。

double 格式布局:|s(1)|exp(11)|frac(52)|,Bias=1023。

3.9.5 ⑤ 图片说明

建议插入的图片

  • 单精度浮点数和双精度浮点数的位布局对比图(各字段的大小标注)
  • 三种类型(规格化、非规格化、特殊值)的分类图
  • 8 位迷你浮点格式(4位阶码+3位尾数)的所有非负可表示值分布图:0 附近密集,远离 0 越来越稀疏
3.9.6 ⑥ 容易混淆的概念
对比维度 整数(补码) 浮点数(IEEE 754) 最关键区别
表示 精确表示有限范围内的整数 近似表示实数 整数精确,浮点近似
分布 均匀分布(相邻差 1) 不均匀(接近 0 密度大) 浮点间距随数值大小变化
溢出 无符号→模 2^w;补码→符号翻转 溢出→±∞ 浮点溢出有专门的"无穷"值
0 的表示 只有一个 0 有 +0 和 -0 两个 浮点的 ±0 在某些比较中被视为相等
3.9.7 ⑦ 面试会怎么问

问题 :为什么 0.1 + 0.2 != 0.3

回答 :0.1 和 0.2 在 IEEE 754 二进制浮点格式中都无法精确表示------它们都是无限循环的二进制小数,只能取近似值。0.10.100000000000000005550.20.20000000000000001110,两者的浮点近似值之和略微偏离 0.3 的浮点近似值,所以 0.1 + 0.2 == 0.3 为假。

3.9.8 ⑧ 一句话总结

!note

浮点数 = 科学记数法的二进制版:(-1)^s × M × 2^E。它范围大但精度有限,靠近 0 更密集,远离 0 越稀疏。0.1 + 0.2 != 0.3 不是 bug,而是"有限位数的二进制表示"的必然结果。

3.9.9 知识点 9 自测 🧠
3.9.9.1 基础题(5 题)
  1. IEEE 754 单精度 float 的符号位、阶码位、尾数位各占多少位?
  2. 规格化数的尾数 M 是 1 + f 还是 f
  3. float 的 Bias(偏置量)是多少?
  4. 阶码全 1、尾数非零代表什么值?
  5. NaN 的全称是什么?
3.9.9.2 理解题(5 题)
  1. 为什么规格化数尾数要"隐含 1"(即 M = 1 + f)?
  2. 为什么靠近 0 的浮点数比远离 0 的更密集?
  3. 非规格化数的两个用途是什么?
  4. 为什么有 +0 和 -0 两种"零"?它们在什么情况下的行为不同?
  5. 8 位浮点格式(k=4 阶码位,n=3 尾数位),Bias 是多少?它能表示的最小正规格化数是多少?
3.9.9.3 思考题(3 题)
  1. 如果让 IEEE 754 的尾数位数翻倍,会影响什么?
  2. 为什么"爱国者导弹拦截失败"与浮点精度有关?
  3. 有没有实数在 IEEE 754 浮点中可以被精确表示?
3.9.9.4 面试/表达题(5 题)
  1. 画图解释单精度浮点数的位布局及各字段含义。
  2. 将十进制 0.25 转为二进制浮点表示。
  3. 为什么 float 的有效数字大约是 7 位十进制数字?
  4. 解释"规格化数"、"非规格化数"、"∞"、"NaN"四类值的编码规则。
  5. 浮点数的"逐渐下溢"(gradual underflow)是什么意思?为什么它比直接跳到 0 更好?

点击查看答案

基础题

  1. s=1, exp=8, frac=23。
  2. M = 1 + f(隐含开头的 1)。
  3. 127(= 2^(8-1) - 1 = 2^7 - 1)。
  4. NaN(Not a Number)。
  5. Not a Number。

理解题

  1. "隐含 1"是一个技巧:总是可以调整阶码,使尾数的二进制小数点前恰好是 1.xxx。既然第一位永远是 1,就不需要浪费一位来存它,这就多了一位精度。
  2. 因为阶码 E 决定了"步长"------规格化数中相邻两个数的间距是 2^E × 2^(-n),E 越小(数越小),间距越小。非规格化数更加密集于 0 附近。
  3. (1)表示数值 0(+0 和 -0);(2)表示非常接近 0 的数(逐渐下溢)------填补了 0 和最小规格化数之间的缺口。
  4. IEEE 754 在语义上区分 +0 和 -0:1/(+0) = +∞1/(-0) = -∞。但它们相等:+0 == -0 为真。在某些极限计算中有意义。
  5. Bias = 2^(4-1) - 1 = 7。最小正规格化数:E = 1-7 = -6,M=1+0=1,V=2^(-6)×1=1/64≈0.015625。

思考题

  1. 精度提高(能区分更小的数之间的差异),但存储空间增大,计算效率降低。double 的 52 位尾数已经是大多数科学计算的平衡点。
  2. 爱国者导弹系统用 24 位寄存器存储 1/10 的近似值,每 0.1 秒加一次。运行 100 小时后,累积误差约 0.34 秒。飞毛腿导弹速度 2000 米/秒,0.34 秒就是 680 米的偏差,导致拦截失败。
  3. 有的。任何可以写成 x × 2^y(x、y 为整数,x 的二进制位数不超过尾数位数+1)的数都可以精确表示。例如 0.5、0.25、0.125、1.0、2.0、1024 等都可以精确表示。

面试/表达题

  1. float 的 32 位:最高位 s=符号,接下来 8 位 exp=阶码(含偏置 127),最后 23 位 frac=尾数的小数部分。值 = (-1)^s × M × 2^E,其中 M 和 E 的取值由 exp 是否全 0 或全 1 决定。
  2. 0.25 = 1/4 = 2^(-2) = 1.0 × 2^(-2)。符号 s=0。E = -2,e = E + Bias = -2 + 127 = 125 = 01111101。M = 1.0,f = 0。位模式:0 01111101 00000000000000000000000。
  3. 23 位尾数 + 隐含 1 = 24 位二进制有效数字 ≈ 24 × log10(2) ≈ 7.22 位十进制有效数字。所以 float 大约能保证 7 位十进制精度。
  4. 规格化(exp 不全 0 不全 1):E=e-Bias,M=1+f。非规格化(exp 全 0):E=1-Bias,M=f。∞(exp 全 1,frac 全 0)。NaN(exp 全 1,frac 非零)。
  5. 如果没有非规格化数,0 和最小规格化数之间会有一个"跳跃"(约 1.2×10^(-38) 的突变)。非规格化数用"逐渐变小"的尾数填补这个区间,让数值分布均匀地接近 0,这在数值计算中(如迭代接近 0 的过程)非常重要。

4 本章完整核心过程 🔄

本章的核心过程是:"一个 C 语言整数的生命历程"------从源代码到内存,再到运算结果。
#mermaid-svg-4jYGm9iS5JSM4JdW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4jYGm9iS5JSM4JdW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4jYGm9iS5JSM4JdW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4jYGm9iS5JSM4JdW .error-icon{fill:#552222;}#mermaid-svg-4jYGm9iS5JSM4JdW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4jYGm9iS5JSM4JdW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4jYGm9iS5JSM4JdW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4jYGm9iS5JSM4JdW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4jYGm9iS5JSM4JdW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4jYGm9iS5JSM4JdW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4jYGm9iS5JSM4JdW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4jYGm9iS5JSM4JdW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4jYGm9iS5JSM4JdW .marker.cross{stroke:#333333;}#mermaid-svg-4jYGm9iS5JSM4JdW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4jYGm9iS5JSM4JdW p{margin:0;}#mermaid-svg-4jYGm9iS5JSM4JdW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4jYGm9iS5JSM4JdW .cluster-label text{fill:#333;}#mermaid-svg-4jYGm9iS5JSM4JdW .cluster-label span{color:#333;}#mermaid-svg-4jYGm9iS5JSM4JdW .cluster-label span p{background-color:transparent;}#mermaid-svg-4jYGm9iS5JSM4JdW .label text,#mermaid-svg-4jYGm9iS5JSM4JdW span{fill:#333;color:#333;}#mermaid-svg-4jYGm9iS5JSM4JdW .node rect,#mermaid-svg-4jYGm9iS5JSM4JdW .node circle,#mermaid-svg-4jYGm9iS5JSM4JdW .node ellipse,#mermaid-svg-4jYGm9iS5JSM4JdW .node polygon,#mermaid-svg-4jYGm9iS5JSM4JdW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4jYGm9iS5JSM4JdW .rough-node .label text,#mermaid-svg-4jYGm9iS5JSM4JdW .node .label text,#mermaid-svg-4jYGm9iS5JSM4JdW .image-shape .label,#mermaid-svg-4jYGm9iS5JSM4JdW .icon-shape .label{text-anchor:middle;}#mermaid-svg-4jYGm9iS5JSM4JdW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4jYGm9iS5JSM4JdW .rough-node .label,#mermaid-svg-4jYGm9iS5JSM4JdW .node .label,#mermaid-svg-4jYGm9iS5JSM4JdW .image-shape .label,#mermaid-svg-4jYGm9iS5JSM4JdW .icon-shape .label{text-align:center;}#mermaid-svg-4jYGm9iS5JSM4JdW .node.clickable{cursor:pointer;}#mermaid-svg-4jYGm9iS5JSM4JdW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4jYGm9iS5JSM4JdW .arrowheadPath{fill:#333333;}#mermaid-svg-4jYGm9iS5JSM4JdW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4jYGm9iS5JSM4JdW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4jYGm9iS5JSM4JdW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4jYGm9iS5JSM4JdW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4jYGm9iS5JSM4JdW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4jYGm9iS5JSM4JdW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4jYGm9iS5JSM4JdW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4jYGm9iS5JSM4JdW .cluster text{fill:#333;}#mermaid-svg-4jYGm9iS5JSM4JdW .cluster span{color:#333;}#mermaid-svg-4jYGm9iS5JSM4JdW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4jYGm9iS5JSM4JdW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4jYGm9iS5JSM4JdW rect.text{fill:none;stroke-width:0;}#mermaid-svg-4jYGm9iS5JSM4JdW .icon-shape,#mermaid-svg-4jYGm9iS5JSM4JdW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4jYGm9iS5JSM4JdW .icon-shape p,#mermaid-svg-4jYGm9iS5JSM4JdW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4jYGm9iS5JSM4JdW .icon-shape .label rect,#mermaid-svg-4jYGm9iS5JSM4JdW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4jYGm9iS5JSM4JdW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4jYGm9iS5JSM4JdW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4jYGm9iS5JSM4JdW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 大端法
小端法
x + 1
x * 2
与 unsigned 混用
C语言源代码中声明 int x = -12345
编译器确定目标字长和字节序
编码为补码位模式

-12345 → 0xCFC7 16位
存储在内存中
高地址: 0xC7, 低地址?: 0xCF
低地址: 0xC7, 高地址?: 0xCF
运行时加载到寄存器
参与运算
0xCFC7 + 0x0001

= 0xCFC8 = -12344
编译器优化为 x << 1

0xCFC7 << 1 = 0x9F8E
隐式转换为 4294952951

此时 -1 < 0U 为假!

逐步说明

  1. 声明→编码 :编译器根据目标平台(32 位/64 位)和数据类型的"典型大小"确定用几个字节。int 通常是 4 字节(32 位),编码为补码------最高位是符号位(负权)。
  2. 编码→存储 :把 4 个字节按目标平台的字节序放进内存。Intel/AMD 用小端法(低字节在低地址),数据从内存读取时必须知道字节序才能正确还原。
  3. 存储→运算:CPU 从内存加载到寄存器,ALU 执行算术/逻辑运算。补码加法器不需要知道数字是"有符号"还是"无符号"------位级操作完全相同。
  4. 运算结果→解释:同样的位模式,既可以解释为有符号数(补码),也可以解释为无符号数。当两者混用时,C 语言隐式将两者都转为无符号------这是"潜规则"。
  5. 溢出:整个过程中最容易误解的环节。CPU 不会报错------它只是丢弃超出的位。程序员必须自己检查是否溢出,或使用安全的编程模式。

5 本章重点概念关系图 🔗

#mermaid-svg-szQz1GW108QTOFyD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-szQz1GW108QTOFyD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-szQz1GW108QTOFyD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-szQz1GW108QTOFyD .error-icon{fill:#552222;}#mermaid-svg-szQz1GW108QTOFyD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-szQz1GW108QTOFyD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-szQz1GW108QTOFyD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-szQz1GW108QTOFyD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-szQz1GW108QTOFyD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-szQz1GW108QTOFyD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-szQz1GW108QTOFyD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-szQz1GW108QTOFyD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-szQz1GW108QTOFyD .marker.cross{stroke:#333333;}#mermaid-svg-szQz1GW108QTOFyD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-szQz1GW108QTOFyD p{margin:0;}#mermaid-svg-szQz1GW108QTOFyD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-szQz1GW108QTOFyD .cluster-label text{fill:#333;}#mermaid-svg-szQz1GW108QTOFyD .cluster-label span{color:#333;}#mermaid-svg-szQz1GW108QTOFyD .cluster-label span p{background-color:transparent;}#mermaid-svg-szQz1GW108QTOFyD .label text,#mermaid-svg-szQz1GW108QTOFyD span{fill:#333;color:#333;}#mermaid-svg-szQz1GW108QTOFyD .node rect,#mermaid-svg-szQz1GW108QTOFyD .node circle,#mermaid-svg-szQz1GW108QTOFyD .node ellipse,#mermaid-svg-szQz1GW108QTOFyD .node polygon,#mermaid-svg-szQz1GW108QTOFyD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-szQz1GW108QTOFyD .rough-node .label text,#mermaid-svg-szQz1GW108QTOFyD .node .label text,#mermaid-svg-szQz1GW108QTOFyD .image-shape .label,#mermaid-svg-szQz1GW108QTOFyD .icon-shape .label{text-anchor:middle;}#mermaid-svg-szQz1GW108QTOFyD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-szQz1GW108QTOFyD .rough-node .label,#mermaid-svg-szQz1GW108QTOFyD .node .label,#mermaid-svg-szQz1GW108QTOFyD .image-shape .label,#mermaid-svg-szQz1GW108QTOFyD .icon-shape .label{text-align:center;}#mermaid-svg-szQz1GW108QTOFyD .node.clickable{cursor:pointer;}#mermaid-svg-szQz1GW108QTOFyD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-szQz1GW108QTOFyD .arrowheadPath{fill:#333333;}#mermaid-svg-szQz1GW108QTOFyD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-szQz1GW108QTOFyD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-szQz1GW108QTOFyD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-szQz1GW108QTOFyD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-szQz1GW108QTOFyD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-szQz1GW108QTOFyD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-szQz1GW108QTOFyD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-szQz1GW108QTOFyD .cluster text{fill:#333;}#mermaid-svg-szQz1GW108QTOFyD .cluster span{color:#333;}#mermaid-svg-szQz1GW108QTOFyD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-szQz1GW108QTOFyD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-szQz1GW108QTOFyD rect.text{fill:none;stroke-width:0;}#mermaid-svg-szQz1GW108QTOFyD .icon-shape,#mermaid-svg-szQz1GW108QTOFyD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-szQz1GW108QTOFyD .icon-shape p,#mermaid-svg-szQz1GW108QTOFyD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-szQz1GW108QTOFyD .icon-shape .label rect,#mermaid-svg-szQz1GW108QTOFyD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-szQz1GW108QTOFyD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-szQz1GW108QTOFyD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-szQz1GW108QTOFyD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 位 bit
字节 byte
十六进制 hex
字节序 大端/小端
数据大小 字长
整数类型
无符号数 unsigned
补码 twos-complement
整数运算
溢出 overflow
移位优化 shift
浮点类型 float/double
IEEE 754
规格化/非规格化/特殊值
舍入/精度损失
T2U/U2T转换
隐式类型转换陷阱

关系解释

  • 位→字节→十六进制:这是信息存储的"语言链"。十六进制是二进制和人类之间的翻译桥梁。
  • 字长决定一切:指针大小、地址空间、long 的大小都取决于字长。
  • 无符号数和补码"同根同源":同一个位模式,两种解读。它们在加法、乘法等操作上共享同一套硬件。
  • T2U/U2T 转换是"陷阱工厂":C 语言不改变位模式,只改变解读规则。这导致 -1 < 0U 这种反直觉的结果。
  • 浮点数"另起炉灶":与整数完全不同------三段式编码、指数+尾数、有 ±∞ 和 NaN。浮点运算不满足结合律。

6 本章最容易踩坑的地方 ⚠️

!warning 误区 1

很多人误以为sizeof(int) 在所有机器上都是 4。

实际上 :C 标准只规定 int 至少 16 位,实践中 32 位和 64 位平台通常为 32 位(4 字节),但某些嵌入式系统可能是 16 位。用 int32_t 保证 32 位。

为什么容易错:大部分教程和教学环境默认 32 位,新手从未见过例外。

怎么避免 :永远使用 <stdint.h> 中的定长类型(int32_tuint64_t 等)来保证精确宽度。
!warning 误区 2

很多人误以为&&& 只是写法不同,效果一样。

实际上& 是按位与(返回多位值),&& 是逻辑与(只返回 0 或 1,且有短路求值)。0x10 & 0x01 = 00x10 && 0x01 = 1。在条件判断中误用 & 是常见 bug。

为什么容易错:初学 C 语言时,两者的符号外观太像了。

怎么避免 :记住口诀------"双符号逻辑,单符号位级"。&&/|| 用于 if 条件,&/| 用于操作二进制位。
!warning 误区 3

很多人误以为 :有符号数的右移 >> 是全平台统一的。

实际上:C 标准不保证有符号数右移是算术右移。实际中几乎所有主流编译器都使用算术右移,但从标准角度看,依赖此行为的代码不可移植。

为什么容易错:在 x86/ARM 平台上都测试通过,就以为"肯定没问题"。

怎么避免 :对有符号数右移后检查行为;或先转为无符号数再移位;或使用 int32_t + 明确的算术右移实现。
!warning 误区 4

很多人误以为-1 < 0U 的结果是真。

实际上 :结果为 。因为 -1(有符号)在跟 0U(无符号)比较时,被隐式转换为无符号数:(unsigned)(-1) = 0xFFFFFFFF = UINT_MAX = 4294967295,而 0U = 04294967295 < 0 为假。

为什么容易错:人脑的算术直觉是"负数 < 正数",但 C 语言的隐式转换规则打破了这种直觉。

怎么避免 :不要混用有符号和无符号数进行比较。用 gcc -Wsign-compare -Wconversion 让编译器警告。
!warning 误区 5

很多人误以为:整数溢出时程序会崩溃或抛异常。

实际上 :无符号溢出是定义好的行为(模 2^w),有符号溢出在 C 中是未定义行为------编译器可以假设它不会发生,从而生成"意外"的优化结果。

为什么容易错:Python、Java 等高级语言会抛异常或自动升级精度,C 不会。

怎么避免 :检查溢出条件后再运算;使用编译器内置函数如 __builtin_add_overflow();或转为无符号数做运算。
!warning 误区 6

很多人误以为0.1 + 0.2 == 0.3 应该成立。

实际上:在 IEEE 754 二元浮点格式中,0.1 和 0.2 都无法精确表示,它们的近似值之和的近似值与 0.3 的近似值不相等。

为什么容易错:十进制下 0.1+0.2=0.3 天经地义,但浮点用的是二进制。

怎么避免 :永远不要直接用 == 比较浮点数。用 fabs(a - b) < epsilon 做近似比较;或用 decimal 库做精确十进制小数运算。
!warning 误区 7

很多人误以为 :补码中每个数都有对应的负数(-x)。

实际上INT_MIN(32 位下 -2147483648)在补码中没有对应的正数。-INT_MIN = INT_MIN(溢出)。因为正数范围比负数范围少 1。

为什么容易错 :取绝对值似乎"不应该"改变符号,但数学上 |-2147483648| = 2147483648 超出了 32 位补码的表示范围。

怎么避免 :取绝对值前先判断是否为 INT_MIN;或使用更宽的类型(如 int64_t)来存绝对值。
!warning 误区 8

很多人误以为x << k 在任何 k 下都是安全的。

实际上 :当 k < 0k >= w(字长)时,移位是 C 语言的未定义行为 。例如 1 << 32 在 32 位系统上是未定义的。

为什么容易错 :x86 处理器实际会用移位量 mod 字长来执行(所以 1<<32 在 x86 上等同于 1<<0 = 1),但这只是特定硬件的行为,不是 C 语言的保证。

怎么避免 :确保 k[0, w-1] 范围内;或使用显式的 if 判断。
!warning 误区 9

很多人误以为:float 和 double 在内存中用相同的大端/小端规则存储。

实际上:浮点数的字节序不一定与整数相同。某些 ARM 平台整数用小端、浮点用大端("混合端"),尽管现代平台已很少见。

为什么容易错:所有字节序的相关代码通常只测试整数,假设浮点也一样。

怎么避免 :不要假设整数和浮点的字节序一致;用 unionmemcpy 检查实际存储。
!warning 误区 10

很多人误以为:浮点数的加法满足结合律。

实际上(a + b) + c 不等于 a + (b + c)。例如单精度下 (3.14 + 1e20) - 1e20 = 0.0(3.14 被舍入吞没),而 3.14 + (1e20 - 1e20) = 3.14

为什么容易错:基本的数学直觉是加法可以随便加括号,但浮点的有限精度打破了这一点。

怎么避免:累加时从小到大加、使用双精度(double)积累、或使用专门的补偿求和算法(Kahan summation)。

7 本章自测(总复习)✅

7.1 基础题(5 题)

  1. 0x2F3C 转换为二进制。
  2. 补码中 8 位模式的 11111111 表示什么十进制值?
  3. 在大端法机器上,0x12345678 的第一个字节(最低地址)是什么值?
  4. 无符号数 9 和 12 在 4 位系统上相加的结果是多少?
  5. IEEE 754 单精度浮点数的 Bias 值是多少?

7.2 理解题(5 题)

  1. 为什么十六进制在计算机底层编程中如此常用?
  2. 小端法在 CPU 硬件实现上有什么优势?
  3. 为什么 C 语言中 sizeof 返回的是 size_t(无符号类型)而不是 int
  4. 如何解释"同一个位模式可以表示不同的数值"?
  5. 浮点数为什么不能精确表示 0.1 而整数可以精确表示 0?

7.3 思考题(3 题)

  1. 如果让你设计一个新编程语言,你会如何处理有符号/无符号混用的问题?
  2. 在什么场景下选择 float 而不是 double 是合理的?
  3. 补码的"不对称范围"(负数比正数多 1)在哪些算法中会带来麻烦?

7.4 面试/表达题(5 题)

  1. 解释"补码加法器和无符号加法器是同一个硬件"的原理。
  2. 给一个新手程序员用大白话讲清楚"为什么 -1 < 0U 是假的"。
  3. IEEE 754 浮点数的"规格化"是什么意思?为什么要有这个设计?
  4. 写一个 C 函数,判断一个 32 位的补码整数能否被安全地转为 float 而不丢失精度。
  5. 解释"信息存储"的全貌:从"一个位"到"一个浮点数",逐层说明。

点击查看答案

基础题

  1. 0x2F3C = 0010 1111 0011 1100
  2. 11111111 补码值 = -128 + 127 = -1
  3. 0x12(最高有效字节在最低地址,大端的"大"先来)。
  4. (9 + 12) mod 16 = 21 mod 16 = 5
  5. 127(= 2^7 - 1)。

理解题

  1. 因为每 4 位二进制精确对应 1 位十六进制(16 = 2^4),转换无歧义且压缩率高。十进制和二进制之间没有这种简单的"分组对应"关系。
  2. 加法运算从低位到高位逐位进行(进位向高位传播),小端法把低位放在低地址,和加法的计算顺序自然一致。
  3. 因为 size_t 必须能表示任意对象的最大可能大小(包括整个地址空间),它天然需要无符号且宽度等于指针宽度。
  4. 因为"编码"是解释规则的问题。同一个位模式用 B2U(无符号编码)和 B2T(补码编码)得到不同值。位模式是客观事实,解释规则是主观约定。
  5. 0 = 000...0,这个位模式在所有整数和浮点格式中表示"精确 0"。0.1 在二进制中是 0.0001100110011...(无限循环),有限的尾数只能截取近似。

思考题

  1. Rust 的做法参考:默认不允许隐式有符号↔无符号转换,需要显式的 x as u32。这对于安全性是极大进步,但也增加了书写成本。
  2. 内存受限(嵌入式、GPU 显存)、对精度要求不高(图形渲染的颜色值)、需要大量存储的数组(机器学习中的梯度累加有时用 float16/float)。
  3. 取绝对值、求相反数、二分查找的边界值计算(mid = (low + high) / 2 在极端情况溢出)、安全除法中除数为 INT_MIN 且被除数为 -1 时的处理。

面试/表达题

  1. 补码加法在位级上就是普通的二进制加法(从低位到高位,逐位相加,进位传播,最高位同样处理),最后截断到 w 位。无符号加法也是同样的操作。区别在于"你如何看待结果的位模式"------两个解读函数 B2UB2T 给出不同数值,但位模式是一样的。
  2. C 语言里 -1int(有符号),0Uunsigned。当它们放到一起比较时,C 把 -1 自动当成"无符号数"来看-------1 的二进制全是 1(111...111),当成无符号数就是最大的整数(约 43 亿)。43 亿 < 0?当然不是!所以结果是"假"。
  3. "规格化"就是统一把尾数调整为 1.xxx 的形式(通过调整阶码),让每个数有唯一的规范化表示。好处:(1)消除重复表示;(2)隐含的 1 省出一位精度;(3)简化硬件比较和运算。
  4. float 有 24 位有效二进制位(23 位尾数 + 隐含 1),int 有 32 位。如果 abs(x) 的二进制中"最高非零位到最低位"的跨度超过 24 位,转 float 就会丢失精度。例如 x > 2^24x < -2^24 的 int 转为 float 必然丢失低位。
  5. 最底层是 (0/1)→ 8 位组成一个字节 (byte)→ 多个字节组成 (word),用十六进制 速记 → 字节的排列顺序由字节序 决定 → 同样的字节用不同的编码规则 解读:无符号(纯二进制)、补码(最高位=负权)、IEEE 754 浮点(三段式 V=s×M×2^E)→ 编码后的值参与算术运算,但有限位数会导致溢出精度损失

8 本章速查表 🧾

概念 一句话解释 为什么重要 容易混淆什么
位(bit) 最小存储单位,只有 0/1 两种状态 一切信息的最终形式 字节(byte=8 位)
十六进制 每 4 位二进制对应 1 位 hex 数字 人和位模式之间的翻译桥梁 十进制
大端法 高位字节存在低地址 网络字节序,跨平台数据交换 小端法
小端法 低位字节存在低地址 Intel/AMD 默认,PC 的主流 大端法
补码 最高位是负权的编码方式 C 语言 int 的实际存储方式 无符号数、反码
无符号数 每位都是正权,只能表示非负数 地址、size_t、位掩码等 补码(同一个位模式不同解读)
T2U 转换 补码→无符号,位模式不变,负数变极大的正数 隐式转换的根源 数值转换(真正改变位模式)
溢出 运算结果超出表示范围 安全漏洞(XDR)、火箭爆炸(Ariane 5) 正常截断
IEEE 754 浮点数工业标准,V=s×M×2^E 所有现代 CPU 的 float/double 整数编码
NaN 不是一个数,如 sqrt(-1) 的结果 表示未定义的计算结果 ±∞(无穷大)
规格化数 exp 不全 0 不全 1,M=1+f 浮点数的主体,覆盖绝大多数正常值 非规格化数
算术右移 右移时左边补符号位 保持负数符号,除法优化 逻辑右移(左边补 0)
掩码 用 1 标记"想要"的位 位运算的核心应用,权限、标志位等 普通整数

9 本章总结与下一步 🚀

  • 今天真正学会了什么?

    1. 计算机里的一切------数字、字符、代码------都是位模式 + 编码规则的组合。
    2. 补码是整数的最优编码:硬件加法器可同时处理有符号和无符号运算,不产生"两个零"。
    3. C 语言中有符号和无符号混用是隐式的陷阱:位模式不变、解释规则改变,导致反直觉的运算结果。
    4. 溢出是有限位数的必然结果------CPU 不报错,必须程序员自己检查。
    5. IEEE 754 浮点数V=s×M×2^E 近似实数,精度有限、不可结合,0.1+0.2!=0.3 是设计使然。
  • 下一章会学什么? 第 3 章"程序的机器级表示"------你将看到 C 代码被编译成汇编指令,理解寄存器、指令格式、地址模式,以及补码和位运算在机器代码中的实际使用方式。

  • 建议提前了解什么? 复习十六进制转换(将频繁用到)、熟悉本笔记中的位模式和补码运算、尝试在调试器中查看简单的 C 变量的内存布局(gdbx 命令)。

!tip 30 秒复述挑战

计算机用 0 和 1 存一切。无符号数 把位当纯数字读,补码 让最高位当"负的"来用所以能表示负数,浮点像二进制科学记数法------分符号、指数、有效数字三部分。C 语言里把有符号数和无符号数放一起比较会"自动改为无符号",导致负数变成巨大正数。运算超过位数会溢出而不报错------这是最隐蔽的 bug 来源。

相关推荐
GGMM7891 小时前
施耐德 VX5A1HC90110(VX5A1HD9011)ATV61/ATV71 90‑110KW 电源驱动板详解
笔记·变频器·变频器维修
Wang's Blog2 小时前
PostgreSQL笔记48: 全文检索与向量检索的对比与混合检索实践
笔记·postgresql·全文检索
liuyicenysabel3 小时前
Flask 个人站 iProfile 上线笔记(k3s + Nginx + Let‘s Encrypt)
笔记·nginx·flask
深蓝海拓3 小时前
基于QtPy (PySide6) 的PLC-HMI工程项目(十五)后续完善和改进:PLC端数据解析的防粘包、残包、废包、拆包
笔记·学习·plc
小黄蚁3 小时前
LVGL学习笔记(六)
笔记·学习
Wang's Blog4 小时前
PostgreSQL笔记49:向量检索核心算法、索引调优与过滤策略深度解析
笔记·算法·postgresql
疯狂打码的少年4 小时前
【数据结构】交换类排序:冒泡与快速排序
数据结构·笔记·算法·排序算法
chnyi6_ya4 小时前
VideoHallu 论文阅读笔记
论文阅读·笔记
Wang's Blog5 小时前
PostgreSQL笔记51: 基于 pgvector 构建企业级智能问答系统
数据库·笔记·postgresql