{
    "version": "https://jsonfeed.org/version/1",
    "title": "Stefansky",
    "description": "Stefansky Blog",
    "home_page_url": "https://stefansky07.github.io",
    "items": [
        {
            "id": "https://stefansky07.github.io/post/csapp-chapter-3-machinelevel-representation-of-programs-zceiak.html",
            "url": "https://stefansky07.github.io/post/csapp-chapter-3-machinelevel-representation-of-programs-zceiak.html",
            "title": "CSAPP 第三章 程序的机器级表示",
            "date_published": "2026-05-20T01:29:10.000Z",
            "content_html": "<p><img loading=\"lazy\" src=\"https://raw.githubusercontent.com/Stefansky07/Stefansky07.github.io/hexo-src/source/img/siyuan/1770005577240-20260520092910-fbfeo7l.jpeg\" alt=\"image\" /></p>\n<h1 id=\"csapp-第三章-程序的机器级表示\"><a class=\"anchor\" href=\"#csapp-第三章-程序的机器级表示\">#</a> CSAPP 第三章 程序的机器级表示</h1>\n<p>编译器基于编程语言的规则、操作系统的惯例、目标机器的指令集生成机器代码。</p>\n<p><strong>汇编代码</strong>是机器代码的一种形式，它是机器代码的文本表示。</p>\n<p>高级代码可移植性好，而汇编代码与特定机器密切相关。</p>\n<p>能够阅读汇编代码：</p>\n<p>好处：可以理解编译器的优化能力，并分析代码中隐含的低效率</p>\n<p>条件：<strong>了解编译器将高级语言转换为机器代码的转换方式。</strong></p>\n<p><strong>精通细节很重要，是理解更深和更基本概念的先决条件。要认真研究示例、完成练习。</strong></p>\n<p>32位机器可以使用约 4GB 的随机访问存储器，64位机器可以使用 256TB(2^48) 的内存空间(这里说的是主存)。</p>\n<h2 id=\"32-程序编码\"><a class=\"anchor\" href=\"#32-程序编码\">#</a> <strong>3.2 程序编码</strong></h2>\n<p>汇编器产生的目标代码是机器代码的一种形式，它包含二进制形式表示的所有指令，但还没有填入全局值的地址。</p>\n<h3 id=\"321-机器级代码\"><a class=\"anchor\" href=\"#321-机器级代码\">#</a> <strong>3.2.1 机器级代码</strong></h3>\n<p>影响机器级程序的两种抽象：</p>\n<p><strong>指令集架构</strong>：定义了处理器状态、指令的格式、指令对状态的影响。</p>\n<p><strong>虚拟地址</strong>：机器代码将内存看成一个按字节寻址的数组。</p>\n<p>对机器代码可见的处理器状态：</p>\n<p><strong>程序计数器</strong></p>\n<p><strong>整数寄存器文件</strong>：保存临时数据或重要的程序状态</p>\n<p><strong>条件码寄存器</strong>：保存最近执行的算术或逻辑指令的状态信息。</p>\n<p><strong>一组向量寄存器</strong>：保存一个或多个整数或浮点数值</p>\n<p>C 语言中的数组和结构，在机器代码中用一组连续的字节来表示。</p>\n<p>汇编代码<strong>不区分有符号数和无符号数，不区分指针的不同类型，不区分指针和整数</strong>。</p>\n<p>一条机器指令只执行一个非常基本的操作。</p>\n<h3 id=\"322-代码示例\"><a class=\"anchor\" href=\"#322-代码示例\">#</a> <strong>3.2.2 代码示例</strong></h3>\n<p><strong>反汇编</strong></p>\n<p>使用反汇编器可以根据机器代码产生汇编代码。如：48 89 d3 → mov %rdx,%rbx</p>\n<p>机器代码与反汇编表示的特性：</p>\n<p>x86-64 的指令长度范围为 1~15 字节。常用指令和操作数少的指令所需字节少。</p>\n<p>从十六进制字节值到汇编指令，格式为：某个数字唯一地对应某个汇编指令，比如 mov 指令以 48 开头。</p>\n<p>指令结尾的 'q' 是大小指示符，大多数情况下可以省略。</p>\n<p>从源程序转换来的可执行目标文件中，除了程序过程的代码，还包含启动和终止程序的代码，与操作系统交互的代码。</p>\n<p>链接器的任务之一就是为函数调用找到匹配的函数的可执行代码的位置。</p>\n<h3 id=\"323-关于格式的注解\"><a class=\"anchor\" href=\"#323-关于格式的注解\">#</a> <strong>3.2.3 关于格式的注解</strong></h3>\n<p>在汇编代码中，以 ‘.’ (点) 开头的行是指导汇编器和链接器工作的伪指令。</p>\n<h2 id=\"33-数据格式\"><a class=\"anchor\" href=\"#33-数据格式\">#</a> <strong>3.3 数据格式</strong></h2>\n<p>字节：byte，8位；字：word，16位；双字：double words，32位；四字：quad words，64位。</p>\n<p>对应的指令后缀：movb, movw, movl, movq。</p>\n<p>这里说的都是整数，浮点数使用一组完全不同的指令和寄存器。</p>\n<h2 id=\"34-访问信息\"><a class=\"anchor\" href=\"#34-访问信息\">#</a> <strong>3.4 访问信息</strong></h2>\n<p>一个 64 位 CPU 中包含<strong>一组 16 个存储 64 位值的通用目的寄存器</strong>，用来存储整数和指针。</p>\n<p>16 个寄存器标号为 <strong>rax</strong>​**&lt;sub&gt;rbp，r8&lt;/sub&gt;<strong>​</strong>r15**</p>\n<p>16 个寄存器的低位部分都可以作为字节、字、双字、四字来单独访问。分别表示为 al, ax, eax, rax。</p>\n<p><strong>低位操作的规则：</strong></p>\n<p>将寄存器作为目标位置时，生成字节和字的指令会保持剩下的字节不变</p>\n<p>生成双字的指令会把高位四字节置为 0.</p>\n<p><strong>16个寄存器的作用</strong></p>\n<p><strong>rax：返回值</strong></p>\n<p><strong>rsp：栈指针</strong></p>\n<p><strong>rdi, rsi,</strong>  rdx, rcx,  r8, r9：第 1 到第 6 个参数</p>\n<p><strong>rbx, rbp</strong>, r12~r15：被调用者保存</p>\n<p>r10, r11：调用者保存</p>\n<h3 id=\"341-操作数指示符\"><a class=\"anchor\" href=\"#341-操作数指示符\">#</a> <strong>3.4.1 操作数指示符</strong></h3>\n<p>指令的操作数有三种类型：<strong>立即数</strong>，<strong>寄存器</strong>，<strong>内存引用</strong></p>\n<p><strong>最常用的寻址方式：Imm(rb, ri, s)：Imm + rb + ri*s</strong></p>\n<p>s 为比例因子，只能是 1，2，4，8 中的某一个</p>\n<h3 id=\"342-数据传送指令\"><a class=\"anchor\" href=\"#342-数据传送指令\">#</a> <strong>3.4.2 数据传送指令</strong></h3>\n<p><strong>mov类</strong></p>\n<p>mov 只会更新目的操作数指定的寄存器字节或内存位置。</p>\n<p>mov 类是最简单的数据传送指令，mov 类有 5 种：</p>\n<p>movb, movw, movl：传送字节、字、双字</p>\n<p>movq：传送四字。如果源操作数是立即数，只能是双字，然后符号扩展到四字（假的四字）</p>\n<p>movabsq：传送绝对的四字。只能以立即数作为源操作数，以寄存器为目的。可以传送任意 64 位立即数。</p>\n<p>movq 用来传送<strong>寄存器和内存引用中的四字</strong>，movabsq 用来传送<strong>四字的立即数</strong></p>\n<p>mov 类的源操作数和目的操作数不能同时为内存，即不能将值从内存复制到内存。</p>\n<p>mov 指令中寄存器的大小必须与 mov 的后缀字符大小匹配。</p>\n<p>movb $-17, %al</p>\n<p><strong>movz类</strong></p>\n<p>movz 系列和 movs 系列可以把较小的源值复制到较大的目的，<strong>目的都是寄存器</strong>。</p>\n<p>movz 将目的寄存器剩余字节做<strong>零扩展</strong>，movs 做<strong>符号扩展</strong></p>\n<p>movz类：movzbw, movzbl, movzbq, movzwl, movzwq（movzbw 即从字节复制到字，其他类似）</p>\n<p>movs类：movsbw, movsbl, movsbq, movswl, movswq, movslq, cltq</p>\n<p><strong>cltq：</strong> 没有操作数，将 eax 符号扩展到 rax，等价于 movslq %eax,%rax</p>\n<h3 id=\"343-数据传送示例\"><a class=\"anchor\" href=\"#343-数据传送示例\">#</a> <strong>3.4.3 数据传送示例</strong></h3>\n<p>局部变量通常保存在寄存器中。</p>\n<p>函数返回指令 ret 返回的值为<strong>寄存器 rax</strong> 中的值</p>\n<p><strong>强制类型转换</strong>是通过 mov 指令实现的。</p>\n<p>当指针存在寄存器中时，a = *p 的汇编指令为： mov (rdi), rax</p>\n<h3 id=\"344-压入和弹出栈数据\"><a class=\"anchor\" href=\"#344-压入和弹出栈数据\">#</a> <strong>3.4.4 压入和弹出栈数据</strong></h3>\n<p><strong>栈向下增长</strong>，栈顶的地址是栈中元素地址中最低的。栈指针 rsp 保存栈顶元素的地址。</p>\n<p>出入栈指令：</p>\n<p>pushq rax：压栈，<strong>栈指针减 8</strong>  并将 rax 中的值写入新的栈顶地址，等价于：subq $8, (rsp) ; movq rax,(rsp)。</p>\n<p>popq rax：出栈，<strong>栈指针加 8</strong>  并将出栈的值写入 rax 中，等价于：movq (rsp),rax ; add $8,(rasp)</p>\n<p>使用 mov 指令和标准的内存寻址方法可以访问<strong>栈内的任意位置</strong>，而非仅限于栈顶。</p>\n<h2 id=\"35-算术和逻辑操作\"><a class=\"anchor\" href=\"#35-算术和逻辑操作\">#</a> <strong>3.5 算术和逻辑操作</strong></h2>\n<p>x86-64 的每个指令类都有<strong>对应四种不同大小数据</strong>的指令</p>\n<p>算术和逻辑操作共有四组：</p>\n<p>加载有效地址</p>\n<p>leaq S, D：将 S 的地址保存到 D 中，D 必须是寄存器</p>\n<p>一元操作</p>\n<p>inc D: D+1</p>\n<p>dec D: D-1</p>\n<p>neg D：取负</p>\n<p>not D：取补</p>\n<p>二元操作（加减乘，与或异或，没有除法）</p>\n<p>add s, d: d=d+s</p>\n<p>sub s, d:  d=d-s</p>\n<p>imul s, d: d=d*s 乘</p>\n<p>xor s, d: d=d^s 异或</p>\n<p>or s, d: d=d|s 或</p>\n<p>and s,d: d=d&amp;s 与</p>\n<p>移位</p>\n<p>sal k,d: d=d&lt;&lt;k 左移</p>\n<p>shl k,d: d=d&lt;&lt;k 左移，等同于 sal</p>\n<p>sar k,d: d=d&lt;&lt;k 左移，算术右移，左端补符号位</p>\n<p>shr k,d: d=d&lt;&lt;k 左移，逻辑右移，左端补零</p>\n<h3 id=\"351-加载有效地址\"><a class=\"anchor\" href=\"#351-加载有效地址\">#</a> <strong>3.5.1 加载有效地址</strong></h3>\n<p>leaq 实际上是 movq 指令的变形。操作是从内存读数据地址到寄存器。</p>\n<p>leaq 在实际应用中常常不用来取地址，而用来计算加法和有限形式的乘法</p>\n<p>leaq 9(rdi, rsi, 4), rax;//x in rdi,y in rsi。此操作实际上等于将 x+4*y+9 的结果存入 rax</p>\n<h3 id=\"352-一元和二元操作\"><a class=\"anchor\" href=\"#352-一元和二元操作\">#</a> <strong>3.5.2 一元和二元操作</strong></h3>\n<p>一元操作中的操作数既是源又是目的。</p>\n<p>二元操作中的第二个操作数既是源又是目的。</p>\n<p>因为不能从内存到内存，因此当第二个操作数是内存地址时，要先从内存读出值，执行操作后再把结果写回去。</p>\n<p>注意 sub s,d 是 d-s 而不是 s-d</p>\n<h3 id=\"353-移位操作\"><a class=\"anchor\" href=\"#353-移位操作\">#</a> <strong>3.5.3 移位操作</strong></h3>\n<p>移位操作的移位量可以是一个立即数或放在单字节寄存器 cl 中。</p>\n<p>当移位量大于目的数的长度时，只取移位量低字节中的值（小于目的数长度）来作为真实的移位量。</p>\n<h3 id=\"354-特殊的算术操作\"><a class=\"anchor\" href=\"#354-特殊的算术操作\">#</a> <strong>3.5.4 特殊的算术操作</strong></h3>\n<p>两个 64 位数的乘积需要 128 位来表示，x86-64指令集可以有限的支持对 128 位数的操作，包括乘法和除法。</p>\n<p>128 位数需要两个寄存器来存储，移动时也需要两个 movq 指令来移动。</p>\n<p>这种情况对于有符号数和无符号数采用了不同的指令。</p>\n<h2 id=\"36-控制\"><a class=\"anchor\" href=\"#36-控制\">#</a> <strong>3.6 控制</strong></h2>\n<p>条件语句、循环语句、分支语句都要求有条件的执行。</p>\n<p>机器代码提供两种低级机制来实现有条件的行为：</p>\n<p><strong>测试数据值</strong>，然后根据测试的结果来改变控制流或数据流</p>\n<p><strong>使用 jump 指令进行跳转</strong></p>\n<h3 id=\"361-条件码\"><a class=\"anchor\" href=\"#361-条件码\">#</a> <strong>3.6.1 条件码</strong></h3>\n<p><strong>条件码寄存器</strong>都是单个位的，是不同于整数寄存器的另一组寄存器。</p>\n<p>条件码描述了最近的算术或逻辑操作的属性，可以通过检测这些寄存器来执行条件分支指令。</p>\n<p>常用条件码：</p>\n<p><strong>CF：进位标志。</strong> 最近的操作使最高位产生了进位。可以用来检查无符号数的溢出</p>\n<p><strong>ZF：零标志。</strong> 最近的操作的结果为 0</p>\n<p><strong>SF：符号标志。</strong> 最近的操作的结果为负数。</p>\n<p><strong>OF：溢出标志。</strong> 最近的操作导致了补码溢出</p>\n<p>除了 leaq 指令外，其余的所有算术和逻辑指令都会<strong>根据运算结果设置条件码</strong>。</p>\n<p>此外还有两类特殊的指令，他们只设置条件码不更新目的寄存器：</p>\n<p><strong>cmp s1, s2：</strong> 除了不更新目的寄存器外与 sub 指令的行为相同</p>\n<p><strong>test s1, s2：</strong> 除了不更新目的寄存器外与 and 指令的行为相同</p>\n<h3 id=\"362-访问条件码\"><a class=\"anchor\" href=\"#362-访问条件码\">#</a> <strong>3.6.2 访问条件码</strong></h3>\n<p>条件码一般不直接读取，常用的使用方法有 3 种：</p>\n<p>根据条件码的某种组合，使用 set 指令类将一个字节设置为 0 或 1。</p>\n<p>条件跳转到程序的某个其他部分</p>\n<p>有条件地传送数据</p>\n<p><strong>set 指令类</strong></p>\n<p>set 指令的目的操作数是低位单字节寄存器元素或一个字节的内存位置。set 会将该字节设置为 0 或 1</p>\n<p>set 指令类的后缀指明了所考虑的条件码的组合，如 setl (set less) 表示“小于时设置”</p>\n<p><img loading=\"lazy\" src=\"https://raw.githubusercontent.com/Stefansky07/Stefansky07.github.io/hexo-src/source/img/siyuan/wps1-20250310214031-vpdch3a.jpg\" alt=\"\" /></p>\n<p>注意到上图中，set 指令对于大于、小于的比较分为了有符号和无符号两类。</p>\n<p>大多数时候，机器代码对无符号和有符号两种情况使用一样的指令。</p>\n<p>使用不同指令来处理无符号和有符号操作的情况：</p>\n<p>不同的条件码组合：</p>\n<p>不同版本的右移：sar 和 shr</p>\n<p>不同的乘法和除法指令</p>\n<p>汇编语言中数据本身不区分有符号和无符号，通过不同的指令来区分有符号操作和无符号操作。</p>\n<p>注意在汇编代码中，8字节的操作数可能是 long，long long 或 指针</p>\n<h3 id=\"363-跳转指令\"><a class=\"anchor\" href=\"#363-跳转指令\">#</a> <strong>3.6.3 跳转指令</strong></h3>\n<p>跳转指令的目的地由一个标号指明</p>\n<pre><code>jmp .L1 ;//跳转到 .L1 。在实际的跳转指令中，.L1 会直接编码为跳转目标的地址。\n\nmovq (rax),rdx\n</code></pre>\n<p>.L1:</p>\n<pre><code>popq rdx\n</code></pre>\n<p>jmp 可以是直接跳转，即操作数为标号。也可以间接跳转，即操作数是寄存器或内存引用，这种情况下跳转到寄存器中存储的地址处。</p>\n<p>跳转指令分为<strong>有条件跳转</strong>和<strong>无条件跳转</strong>，只有 jmp 是无条件跳转。有条件跳转都只能是直接跳转。</p>\n<p>有条件跳转类似 set 指令系列，根据条件码寄存器的值来判断是否进行跳转。</p>\n<p><img loading=\"lazy\" src=\"https://raw.githubusercontent.com/Stefansky07/Stefansky07.github.io/hexo-src/source/img/siyuan/wps2-20250310214031-rg72llj.jpg\" alt=\"\" /></p>\n<h3 id=\"364-跳转指令的编码\"><a class=\"anchor\" href=\"#364-跳转指令的编码\">#</a> <strong>3.6.4 跳转指令的编码</strong></h3>\n<p><strong>跳转指令的机器编码</strong>（就是纯粹数字表示的机器语言）有几种方式，其中两种如下：</p>\n<p><strong>PC 相对跳转</strong>：使用目标地址与跳转指令之后下一条指令的地址之间的差来编码。可以用 1、2 或 4 个字节来编码。</p>\n<p><strong>绝对地址编码</strong>：使用目标的绝对地址。用 4 个字节直接指出。</p>\n<p>汇编器和链接器会自己选择适当的编码方式</p>\n<h3 id=\"365-用条件控制来实现条件分支\"><a class=\"anchor\" href=\"#365-用条件控制来实现条件分支\">#</a> <strong>3.6.5 用条件控制来实现条件分支</strong></h3>\n<p>汇编代码层面的条件控制类似于 c 语言的 goto 语句。</p>\n<p>汇编语言使用条件码和条件跳转来起到和 c 语言中 if 相似的作用</p>\n<p>'C 语言'</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-c\"><span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">if</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> x</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">y </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#123;</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> i</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">++</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#125;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">else</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#123;</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> i</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">--</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#125;</span></span></code></pre>\n<p>'汇编'</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>cmpq rsi,rdi</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>jge .L2</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>incl rax;</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>.L2:</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>    decl rax;</span></span></code></pre>\n<h3 id=\"366-用条件传送来实现条件分支\"><a class=\"anchor\" href=\"#366-用条件传送来实现条件分支\">#</a> <strong>3.6.6 用条件传送来实现条件分支</strong></h3>\n<p><strong>条件传送</strong>不会改变控制流，而是根据条件码决定是否改写目的寄存器。</p>\n<p>常见指令形式为 <strong>cmovXX S, D</strong></p>\n<p>其中源操作数可以是寄存器或内存，<strong>目的操作数只能是寄存器</strong></p>\n<p>条件传送常用于实现<strong>简单的条件表达式</strong>，例如：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>v = x &#x3C; y ? x : y;</span></span></code></pre>\n<p>使用条件传送的好处：</p>\n<p>避免分支预测失败带来的代价</p>\n<p>在两种结果都容易提前算出的情况下，通常比真正跳转更高效</p>\n<p>条件传送的局限：</p>\n<p>两个分支对应的值通常都要先算出来</p>\n<p>如果某一分支有副作用，或者计算代价很大，就不适合用条件传送</p>\n<h3 id=\"367-循环\"><a class=\"anchor\" href=\"#367-循环\">#</a> <strong>3.6.7 循环</strong></h3>\n<p>机器级代码中没有 for、while、do-while 这样的“循环语法”，只有<strong>测试 + 跳转</strong></p>\n<p><strong>do-while</strong> 最自然，因为它本来就是“先执行一次，再判断”</p>\n<p>它通常会被翻译成：</p>\n<p>执行循环体</p>\n<p>测试条件</p>\n<p>条件成立时跳回循环开头</p>\n<p><strong>while</strong> 往往会被改写成带条件跳转的 do-while 形式</p>\n<p>也就是先跳到测试代码，再根据测试结果决定是否进入循环体</p>\n<p><strong>for</strong> 本质上也可以拆成：</p>\n<p>初始化部分</p>\n<p>条件测试部分</p>\n<p>循环体部分</p>\n<p>更新部分</p>\n<p>因此在汇编层面，for 和 while 通常没有本质区别</p>\n<p><strong>break 和 continue</strong></p>\n<p>break 对应<strong>跳到循环结束位置</strong></p>\n<p>continue 对应<strong>跳到下一轮测试或更新位置</strong></p>\n<h3 id=\"368-switch语句\"><a class=\"anchor\" href=\"#368-switch语句\">#</a> <strong>3.6.8 switch语句</strong></h3>\n<p>switch 语句是一种<strong>多重分支</strong></p>\n<p>编译器实现 switch 的方式主要有两种：</p>\n<p><strong>使用一连串 cmp 和 jump</strong></p>\n<p><strong>使用跳转表（jump table）</strong></p>\n<p>当 case 的值分布比较稠密时，编译器更倾向于使用<strong>跳转表</strong></p>\n<p>做法通常是：</p>\n<p>先检查开关值是否越界</p>\n<p>若越界则跳到 default</p>\n<p>若不越界，则用开关值作为索引，从表中取出目标地址并进行<strong>间接跳转</strong></p>\n<p>跳转表的本质是一个<strong>地址数组</strong></p>\n<p>当 case 值很稀疏时，使用跳转表会浪费空间，这时编译器通常改用比较和分支</p>\n<h2 id=\"37-过程\"><a class=\"anchor\" href=\"#37-过程\">#</a> <strong>3.7 过程</strong></h2>\n<p>过程调用是机器级程序中最核心的机制之一。</p>\n<p>一个过程调用需要处理 3 件事：</p>\n<p><strong>传递控制</strong></p>\n<p><strong>传递数据</strong></p>\n<p><strong>为局部数据分配和释放空间</strong></p>\n<h3 id=\"371-运行时栈\"><a class=\"anchor\" href=\"#371-运行时栈\">#</a> <strong>3.7.1 运行时栈</strong></h3>\n<p>过程调用依赖于<strong>运行时栈</strong></p>\n<p>当过程 P 调用过程 Q 时，控制和数据都通过栈来组织</p>\n<p>每次过程调用都会在栈上分配一块空间，称为<strong>栈帧</strong></p>\n<p>栈帧中通常保存：</p>\n<p>返回地址</p>\n<p>被保存的寄存器</p>\n<p>局部变量</p>\n<p>为调用其他过程准备的参数空间</p>\n<p>栈向低地址方向增长，因此分配栈帧通常表现为<strong>rsp 减小</strong></p>\n<p>过程返回时，对应栈帧被释放，rsp 恢复</p>\n<h3 id=\"372-转移控制\"><a class=\"anchor\" href=\"#372-转移控制\">#</a> <strong>3.7.2 转移控制</strong></h3>\n<p>过程调用和返回分别由 <strong>call</strong> 和 <strong>ret</strong> 指令实现</p>\n<p><strong>call label</strong></p>\n<p>先把返回地址压入栈中</p>\n<p>再跳转到被调用过程的开始位置</p>\n<p><strong>ret</strong></p>\n<p>从栈中弹出返回地址</p>\n<p>然后跳转到该地址继续执行</p>\n<p>call 可以是：</p>\n<p><strong>直接调用</strong>：目标是一个标号</p>\n<p><strong>间接调用</strong>：目标地址保存在寄存器或内存中。函数指针调用就是这种形式</p>\n<h3 id=\"373-数据传送\"><a class=\"anchor\" href=\"#373-数据传送\">#</a> <strong>3.7.3 数据传送</strong></h3>\n<p>过程之间传递数据主要依赖<strong>寄存器和栈</strong></p>\n<p>x86-64 中，整数和指针参数的前 6 个通常通过寄存器传递：</p>\n<p>rdi, rsi, rdx, rcx, r8, r9</p>\n<p>如果参数超过 6 个，其余参数通过栈传递</p>\n<p>返回值通常通过 <strong>rax</strong> 返回</p>\n<p>有些较大的返回结果不会直接放在寄存器里，而是通过调用者提供的一块内存区域来返回</p>\n<p><strong>调用者保存和被调用者保存</strong></p>\n<p>有些寄存器属于<strong>调用者保存</strong></p>\n<p>如果调用者希望这些值在 call 后仍然有效，就必须自己先保存</p>\n<p>有些寄存器属于<strong>被调用者保存</strong></p>\n<p>如果被调用过程要使用它们，就要先保存，结束前再恢复</p>\n<p>这套约定保证了不同过程编译出来后仍然能够正确协作</p>\n<h3 id=\"374-栈上的局部存储\"><a class=\"anchor\" href=\"#374-栈上的局部存储\">#</a> <strong>3.7.4 栈上的局部存储</strong></h3>\n<p>当局部变量太多，或者必须有内存地址时，编译器会把它们放在<strong>栈帧</strong>里</p>\n<p>例如：</p>\n<p>局部数组</p>\n<p>结构体局部变量</p>\n<p>需要取地址的局部变量</p>\n<p>为了给这些数据分配空间，过程开始时通常会执行类似：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>subq $32, %rsp</span></span></code></pre>\n<p>这表示在栈上开辟 32 字节空间</p>\n<p>函数结束前再通过 addq 或直接恢复 rsp 来释放这些空间</p>\n<h3 id=\"375-寄存器中的局部存储空间\"><a class=\"anchor\" href=\"#375-寄存器中的局部存储空间\">#</a> <strong>3.7.5 寄存器中的局部存储空间</strong></h3>\n<p>如果局部变量是简单的标量值，而且生命周期合适，编译器更愿意把它们保存在<strong>寄存器</strong>里</p>\n<p>这样做的优点是：</p>\n<p>访问速度快</p>\n<p>不需要内存读写</p>\n<p>便于编译器做优化</p>\n<p>因此，优化后的代码里常常看不到“局部变量名”，只能看到若干寄存器在承担变量角色</p>\n<h3 id=\"376-递归过程\"><a class=\"anchor\" href=\"#376-递归过程\">#</a> <strong>3.7.6 递归过程</strong></h3>\n<p>递归并不需要特殊的指令支持。</p>\n<p>它只是一个过程<strong>直接或间接调用自己</strong></p>\n<p>每次递归调用都会产生一个新的栈帧，因此：</p>\n<p>每一层调用都有自己独立的返回地址</p>\n<p>每一层调用都有自己独立的局部变量</p>\n<p>递归之所以可行，本质上依赖于栈对“多个未完成调用”的保存能力</p>\n<p>递归层次过深会导致<strong>栈空间耗尽</strong></p>\n<p>这就是栈溢出的一个常见来源</p>\n<h2 id=\"38-数组分配和访问\"><a class=\"anchor\" href=\"#38-数组分配和访问\">#</a> <strong>3.8 数组分配和访问</strong></h2>\n<p>数组在机器级程序中表现为一段<strong>连续分配的内存</strong></p>\n<p>数组访问的关键问题是：如何从<strong>基址</strong>和<strong>下标</strong>算出元素地址</p>\n<h3 id=\"381-基本原则\"><a class=\"anchor\" href=\"#381-基本原则\">#</a> <strong>3.8.1 基本原则</strong></h3>\n<p>若数组 A 的基址为 x，元素类型大小为 L，那么：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>&#x26;A[i] = x + i * L</span></span></code></pre>\n<p>这说明数组访问本质上是<strong>地址计算</strong></p>\n<p>编译器会尽量利用 x86-64 的比例寻址模式来完成这样的计算</p>\n<p>如果元素大小是 1、2、4、8 字节，那么通常可以直接利用硬件支持的比例因子</p>\n<h3 id=\"382-指针运算\"><a class=\"anchor\" href=\"#382-指针运算\">#</a> <strong>3.8.2 指针运算</strong></h3>\n<p>在 C 语言中，数组名在很多场景下会退化为<strong>指向首元素的指针</strong></p>\n<p>因此：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>A[i]</span></span></code></pre>\n<p>和</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>*(A + i)</span></span></code></pre>\n<p>本质上是同一件事</p>\n<p>指针加法并不是按字节递增，而是按<strong>所指对象的大小</strong>递增</p>\n<p>例如 int *p 中，p+1 实际上是地址加 4（假设 int 为 4 字节）</p>\n<p>两个同类型指针相减，得到的也不是字节差，而是<strong>元素个数差</strong></p>\n<h3 id=\"383-嵌套的数组\"><a class=\"anchor\" href=\"#383-嵌套的数组\">#</a> <strong>3.8.3 嵌套的数组</strong></h3>\n<p>多维数组在内存中仍然是按<strong>线性方式</strong>存放的</p>\n<p>C 语言采用<strong>行优先（row-major）</strong> 次序</p>\n<p>例如二维数组 A<a href=\"#\">R</a> 中，A<a href=\"#\">i</a> 的地址为：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>&#x26;A[i][j] = x + (i * C + j) * L</span></span></code></pre>\n<p>其中 x 是数组起始地址，L 是每个元素的大小</p>\n<p>因此访问二维数组时，编译器要先算出“第 i 行的起始偏移”，再加上列偏移</p>\n<h3 id=\"384-定长数组\"><a class=\"anchor\" href=\"#384-定长数组\">#</a> <strong>3.8.4 定长数组</strong></h3>\n<p>如果数组维度在编译时就已知，那么很多地址计算都可以在编译阶段部分化简</p>\n<p>例如一行有多少字节，可以直接当作常数使用</p>\n<p>这样生成的汇编代码通常更简洁，也更容易优化</p>\n<p>这就是为什么固定维度数组经常能得到更高效的机器代码</p>\n<h3 id=\"385-变长数组\"><a class=\"anchor\" href=\"#385-变长数组\">#</a> <strong>3.8.5 变长数组</strong></h3>\n<p>变长数组（VLA）的维度直到运行时才知道</p>\n<p>这意味着：</p>\n<p>行大小要在运行时计算</p>\n<p>整个数组所需空间也要在运行时计算</p>\n<p>栈帧大小因此不再固定</p>\n<p>编译器通常会动态调整 rsp 来为它分配空间</p>\n<p>由于涉及更多运行时计算，变长数组对应的机器代码往往比定长数组复杂</p>\n<h2 id=\"39-异质的数据结构\"><a class=\"anchor\" href=\"#39-异质的数据结构\">#</a> <strong>3.9 异质的数据结构</strong></h2>\n<p>数组中的元素类型完全相同，而结构体和联合允许把<strong>不同类型的数据组织在一起</strong></p>\n<p>理解它们的机器级表示，对于分析内存布局很重要</p>\n<h3 id=\"391-结构\"><a class=\"anchor\" href=\"#391-结构\">#</a> <strong>3.9.1 结构</strong></h3>\n<p>结构体的各个字段按照一定顺序存放在一段连续内存中</p>\n<p>每个字段相对于结构起始地址都有一个<strong>固定偏移量</strong></p>\n<p>因此访问结构字段时，机器代码所做的事情就是：</p>\n<p>基址 + 固定偏移</p>\n<p>例如 <code>p-&gt;x</code> 往往会被编译成“先取出 p，再访问某个固定偏移位置”</p>\n<p>嵌套结构的访问，本质上只是多层偏移量的叠加</p>\n<h3 id=\"392-联合\"><a class=\"anchor\" href=\"#392-联合\">#</a> <strong>3.9.2 联合</strong></h3>\n<p>联合（union）的所有字段<strong>共享同一段存储空间</strong></p>\n<p>也就是说，不同字段的起始地址相同</p>\n<p>联合的大小至少要能够容纳它的<strong>最大成员</strong></p>\n<p>联合的用途之一是：</p>\n<p>用同一段位模式，从不同类型的角度来解释数据</p>\n<p>但程序员必须自己保证“当前读取的字段”与“最近写入的字段”在语义上是合理的</p>\n<h3 id=\"393-数据对齐\"><a class=\"anchor\" href=\"#393-数据对齐\">#</a> <strong>3.9.3 数据对齐</strong></h3>\n<p>为了提高存取效率，很多数据对象都会按某种<strong>对齐要求</strong>存放</p>\n<p>例如 4 字节对象通常希望地址是 4 的倍数，8 字节对象通常希望地址是 8 的倍数</p>\n<p>对齐会带来一个结果：<strong>填充字节（padding）</strong></p>\n<p>填充可能出现在：</p>\n<p>结构字段之间</p>\n<p>结构体尾部</p>\n<p>结构体尾部补齐的原因是：如果这个结构体再组成数组，那么数组中每个元素的起始地址仍然要满足对齐约束</p>\n<p>因此，字段顺序不同，结构体大小也可能不同</p>\n<h2 id=\"310-在机器级程序中将控制与数据结合起来\"><a class=\"anchor\" href=\"#310-在机器级程序中将控制与数据结合起来\">#</a> <strong>3.10 在机器级程序中将控制与数据结合起来</strong></h2>\n<p>前面分别讨论了控制和数据。</p>\n<p>真正的程序错误，往往出现在二者结合的地方，尤其是<strong>指针、数组、栈帧和控制转移</strong>交织时</p>\n<h3 id=\"3101-理解指针\"><a class=\"anchor\" href=\"#3101-理解指针\">#</a> <strong>3.10.1 理解指针</strong></h3>\n<p>指针保存的是<strong>地址</strong></p>\n<p>但“这个地址该按什么类型解释”，由指针类型决定</p>\n<p>同一个地址：</p>\n<p>作为 char * 读取，含义可能是一个字节</p>\n<p>作为 int * 读取，含义可能是 4 个字节组成的整数</p>\n<p>因此，指针既强大也危险</p>\n<p>空指针、悬垂指针、越界指针都会导致不可预期的行为</p>\n<p>从机器级角度看，处理器只是在使用某个地址，<strong>它并不知道这个地址在 C 语言语义下是否合法</strong></p>\n<h3 id=\"3102-应用使用gdb调试器\"><a class=\"anchor\" href=\"#3102-应用使用gdb调试器\">#</a> <strong>3.10.2 应用:使用GDB调试器</strong></h3>\n<p>GDB 可以帮助我们把高级语言和机器级执行过程联系起来</p>\n<p>常见用途：</p>\n<p>查看寄存器：<code>info registers</code></p>\n<p>反汇编函数：<code>disas</code></p>\n<p>查看内存：<code>x/16gx addr</code></p>\n<p>单步执行：<code>stepi</code>​、<code>nexti</code></p>\n<p>设置断点：<code>break</code></p>\n<p>用 GDB 观察程序时，可以清楚地看到：</p>\n<p>参数进入了哪些寄存器</p>\n<p>栈帧是如何变化的</p>\n<p>条件跳转究竟依据了什么条件码</p>\n<p>这对于理解汇编代码和排查底层错误都非常重要</p>\n<h3 id=\"3103-内存越界引用和缓冲区溢出\"><a class=\"anchor\" href=\"#3103-内存越界引用和缓冲区溢出\">#</a> <strong>3.10.3 内存越界引用和缓冲区溢出</strong></h3>\n<p>数组和缓冲区本质上都是连续内存区域</p>\n<p>如果程序写入超过边界的数据，就会覆盖相邻内存内容</p>\n<p>这可能破坏：</p>\n<p>其他局部变量</p>\n<p>保存的寄存器值</p>\n<p>返回地址</p>\n<p>这类错误之所以危险，是因为机器指令通常<strong>不会自动进行边界检查</strong></p>\n<p>因此一个看似普通的数组写操作，可能最终改变控制流</p>\n<h3 id=\"3104-对抗缓冲区溢出攻击\"><a class=\"anchor\" href=\"#3104-对抗缓冲区溢出攻击\">#</a> <strong>3.10.4 对抗缓冲区溢出攻击</strong></h3>\n<p>现代系统使用了多种机制来减轻缓冲区溢出的危害：</p>\n<p><strong>栈随机化（ASLR）</strong></p>\n<p><strong>栈不可执行</strong></p>\n<p><strong>栈保护值（canary）</strong></p>\n<p>这些机制的作用是：</p>\n<p>增加攻击者预测地址的难度</p>\n<p>阻止把栈中的数据直接当成代码执行</p>\n<p>在返回前检查栈帧是否被破坏</p>\n<p>但最根本的方法仍然是：<strong>写出不发生越界访问的程序</strong></p>\n<h3 id=\"3105-支持变长栈帧\"><a class=\"anchor\" href=\"#3105-支持变长栈帧\">#</a> <strong>3.10.5 支持变长栈帧</strong></h3>\n<p>有些过程的栈帧大小在编译时并不固定，例如：</p>\n<p>使用变长数组</p>\n<p>使用 alloca 在运行时申请栈空间</p>\n<p>这会让 rsp 在过程执行期间发生额外变化</p>\n<p>在这种情况下，如果仍然需要稳定地访问某些局部变量或保存值，编译器更可能使用 <strong>rbp</strong> 作为帧指针</p>\n<p>也就是说：</p>\n<p>rsp 更适合表示“当前栈顶”</p>\n<p>rbp 更适合表示“本过程栈帧中的固定参考位置”</p>\n<h2 id=\"311-浮点代码\"><a class=\"anchor\" href=\"#311-浮点代码\">#</a> <strong>3.11 浮点代码</strong></h2>\n<p>浮点数据的处理和整数明显不同。</p>\n<p>x86-64 中，现代编译器通常使用 <strong>XMM 寄存器</strong> 和一套专门的浮点指令来处理单精度和双精度数</p>\n<h3 id=\"3111-浮点传送和转换操作\"><a class=\"anchor\" href=\"#3111-浮点传送和转换操作\">#</a> <strong>3.11.1 浮点传送和转换操作</strong></h3>\n<p>浮点数据的传送常使用：</p>\n<p><code>movss</code>：传送单精度浮点数</p>\n<p><code>movsd</code>：传送双精度浮点数</p>\n<p>整数和浮点数之间的转换则使用专门的转换指令</p>\n<p>例如：</p>\n<p><code>cvtsi2sd</code>：整数转双精度</p>\n<p><code>cvttsd2si</code>：双精度转整数（带截断）</p>\n<p>这说明浮点和整数虽然都存放在寄存器里，但它们的处理路径并不相同</p>\n<h3 id=\"3112-过程中的浮点代码\"><a class=\"anchor\" href=\"#3112-过程中的浮点代码\">#</a> <strong>3.11.2 过程中的浮点代码</strong></h3>\n<p>浮点参数和返回值通常通过 <strong>XMM 寄存器</strong> 传递</p>\n<p>常见约定是：</p>\n<p>前若干个浮点参数放在 xmm0 ~ xmm7</p>\n<p>浮点返回值放在 xmm0</p>\n<p>因此一个过程如果同时接收整数参数和浮点参数，编译器需要分别管理两套寄存器约定</p>\n<h3 id=\"3113-浮点运算操作\"><a class=\"anchor\" href=\"#3113-浮点运算操作\">#</a> <strong>3.11.3 浮点运算操作</strong></h3>\n<p>浮点加减乘除也有各自的专用指令，例如：</p>\n<p><code>addss</code>​ / <code>addsd</code></p>\n<p><code>subss</code>​ / <code>subsd</code></p>\n<p><code>mulss</code>​ / <code>mulsd</code></p>\n<p><code>divss</code>​ / <code>divsd</code></p>\n<p>这些指令处理的是浮点编码后的位模式，但语义遵循 IEEE 浮点规则</p>\n<p>因此结果会受到<strong>舍入、无穷大、NaN</strong> 等规则影响</p>\n<h3 id=\"3114-定义和使用浮点常数\"><a class=\"anchor\" href=\"#3114-定义和使用浮点常数\">#</a> <strong>3.11.4 定义和使用浮点常数</strong></h3>\n<p>和整数立即数不同，很多浮点常数不会直接编码在运算指令里</p>\n<p>编译器通常把它们放在某个只读数据区中</p>\n<p>使用时先把常数加载到寄存器，再参与计算</p>\n<p>所以在反汇编中，经常能看到某条浮点指令访问一个常量表中的地址</p>\n<h3 id=\"3115-在浮点代码中使用位级操作\"><a class=\"anchor\" href=\"#3115-在浮点代码中使用位级操作\">#</a> <strong>3.11.5 在浮点代码中使用位级操作</strong></h3>\n<p>虽然浮点数有自己的算术规则，但它们本质上仍然是一组位</p>\n<p>因此也可以通过某些按位操作来完成特殊处理</p>\n<p>例如：</p>\n<p>修改符号位来实现取负</p>\n<p>清除符号位来实现绝对值</p>\n<p>这种做法依赖于对 IEEE 浮点表示格式的理解</p>\n<h3 id=\"3116-浮点比较操作\"><a class=\"anchor\" href=\"#3116-浮点比较操作\">#</a> <strong>3.11.6 浮点比较操作</strong></h3>\n<p>浮点比较通常使用专门的比较指令，如 <code>ucomiss</code>​、<code>ucomisd</code></p>\n<p>它们会设置条件码，然后再配合 set 或 jump 指令使用</p>\n<p>与整数比较不同的是，浮点比较还要考虑 <strong>NaN</strong></p>\n<p>一旦操作数中出现 NaN，比较结果会涉及“无序（unordered）”这一特殊情况</p>\n<p>因此浮点比较的行为比整数比较更复杂</p>\n<h3 id=\"3117-对浮点代码的观察结论\"><a class=\"anchor\" href=\"#3117-对浮点代码的观察结论\">#</a> <strong>3.11.7 对浮点代码的观察结论</strong></h3>\n<p>浮点代码与整数代码相比，有几个明显特征：</p>\n<p>使用不同的寄存器集合</p>\n<p>使用不同的传送、转换、比较和运算指令</p>\n<p>必须遵守 IEEE 浮点语义，而不能简单套用整数规则</p>\n<p>因此分析浮点程序时，不能只靠“整数运算直觉”去理解它</p>\n<h3 id=\"312-小结\"><a class=\"anchor\" href=\"#312-小结\">#</a> <strong>3.12 小结</strong></h3>\n<p>本章讨论了 C 程序如何被翻译成机器级表示。</p>\n<p>核心内容包括：</p>\n<p><strong>寄存器和数据传送</strong></p>\n<p><strong>算术与逻辑运算</strong></p>\n<p><strong>条件码、跳转、循环和 switch</strong></p>\n<p><strong>过程调用、栈帧和递归</strong></p>\n<p><strong>数组、结构体、联合和对齐</strong></p>\n<p><strong>缓冲区溢出等底层安全问题</strong></p>\n<p><strong>浮点代码的专用表示方式</strong></p>\n<p>理解这些内容的意义不只是“会读汇编”。</p>\n<p>更重要的是：</p>\n<p>能理解编译器到底做了什么</p>\n<p>能分析程序性能和隐藏的低效率</p>\n<p>能更准确地定位底层 bug</p>\n<p>能从机器级角度理解安全问题产生的原因</p>\n<p>这正是学习“程序的机器级表示”的价值所在</p>\n<p>‍</p>\n",
            "tags": [
                "读书笔记",
                "CSAPP"
            ]
        },
        {
            "id": "https://stefansky07.github.io/post/csapp-chapter-2-data-representation-and-processing.html",
            "url": "https://stefansky07.github.io/post/csapp-chapter-2-data-representation-and-processing.html",
            "title": "CSAPP 第二章 信息的表示和处理",
            "date_published": "2026-05-19T15:53:44.000Z",
            "content_html": "<p><img loading=\"lazy\" src=\"/img/siyuan/2-20260520000529-h6wo8vu.jpg\" alt=\"image\" /></p>\n<h1 id=\"csapp-第二章-信息的表示和处理\"><a class=\"anchor\" href=\"#csapp-第二章-信息的表示和处理\">#</a> CSAPP 第二章 信息的表示和处理</h1>\n<p>第二章 <strong>信息的表示和处理</strong></p>\n<p>计算机使用二值信号存储和表示信息</p>\n<p>当计算结果太大以至于不能表示时，就会产生<strong>溢出</strong>。</p>\n<p>浮点数表示的精度有限，因而浮点运算是不可结合的。</p>\n<p>整数的表示范围小但是精确，浮点数表示的范围大但是是近似的。</p>\n<p>许多安全漏洞是由算术运算的微妙细节导致的。</p>\n<h2 id=\"21-信息存储\"><a class=\"anchor\" href=\"#21-信息存储\">#</a> <strong>2.1 信息存储</strong></h2>\n<p>计算机一般使用字节作为最小的可寻址的内存单位。</p>\n<p>在机器级程序中不包含关于数据类型的信息。</p>\n<p>指针的值是某个存储块的第一个字节的<strong>虚拟地址</strong>。</p>\n<p><strong>每个程序对象可以视为一个字节块。</strong></p>\n<h3 id=\"211-十六进制表示法\"><a class=\"anchor\" href=\"#211-十六进制表示法\">#</a> <strong>2.1.1 十六进制表示法</strong></h3>\n<p>十六进制以 0x 开头。</p>\n<p>A：10；C:12；F：15</p>\n<h3 id=\"212-字数据大小\"><a class=\"anchor\" href=\"#212-字数据大小\">#</a> <strong>2.1.2 字数据大小</strong></h3>\n<p>每个计算机有对应的字长，虚拟地址用一个字来编码，所以<strong>字长决定了虚拟地址空间的大小</strong>。<strong>64 位机器的指针类型长度为 8 字节</strong></p>\n<p>32位机器的虚拟地址空间为 <strong>4GB</strong>，64 位字长的虚拟地址空间位 16 EB。</p>\n<p><strong>int32_t</strong>  和 <strong>int64_t</strong> 类型分别为 4 字节和 8 字节，不受机器影响。使用确定大小的整数类型很有用。</p>\n<p>对 32 位和 64 位机器而言，char、short、int、long long 长度都是一样的，为  1，2，4，8。long 的长度不一样。</p>\n<p>float 和 double 的长度一样，分别为 4，8</p>\n<p>程序对 char 有无符号一般不敏感。</p>\n<h3 id=\"213-寻址和字节顺序\"><a class=\"anchor\" href=\"#213-寻址和字节顺序\">#</a> <strong>2.1.3 寻址和字节顺序</strong></h3>\n<p>对于跨越多字节的对象，它的地址是它所用字节中的<strong>最小地址</strong>。</p>\n<p><strong>两种字节存储法：</strong></p>\n<p><strong>小端法</strong>：数字的低位在前（前就是最小地址）</p>\n<p><strong>大端法</strong>：数字的高位在前</p>\n<p>大多数 Intel 都是小端法，不是所有。</p>\n<h3 id=\"214-表示字符串\"><a class=\"anchor\" href=\"#214-表示字符串\">#</a> <strong>2.1.4 表示字符串</strong></h3>\n<p>C 语言字符串是以 null 字符结尾的字符数组，即 '\\0'</p>\n<p>ASCII 字符适合编码英文文档。</p>\n<p>Unicode（UTF-8）使用 <strong>4 字节</strong>表示字符，一些常用的字符只需要 1 或 2 个字节。所有 ASCII 字符在 UTF-8 中是一样的。</p>\n<p>JAVA 使用 UTF-8 来编码字符串。</p>\n<h3 id=\"215-表示代码\"><a class=\"anchor\" href=\"#215-表示代码\">#</a> <strong>2.1.5 表示代码</strong></h3>\n<p>二进制代码是<strong>不兼容</strong>的，一般无法在不同机器间移植。</p>\n<p>从机器的角度看，<strong>程序就是一个字节序列</strong>。</p>\n<h3 id=\"216-布尔代数\"><a class=\"anchor\" href=\"#216-布尔代数\">#</a> <strong>2.1.6 布尔代数</strong></h3>\n<p><strong>布尔代数</strong>是在 0 和 1 基础上的定义</p>\n<p>可以把字节看作是一个长为 8 的<strong>位向量</strong>。</p>\n<p>位向量的一个应用是表示有限集合。如位向量 [0110 1001] 表示集合 A = {0,3,5,6}。</p>\n<h3 id=\"217-c-语言中的位级运算\"><a class=\"anchor\" href=\"#217-c-语言中的位级运算\">#</a> <strong>2.1.7 C 语言中的位级运算</strong></h3>\n<p>位运算的常见应用是实现<strong>掩码</strong>。掩码表示从一个字中选出的位的集合，如掩码 0xFF 表示一个字的低 8 位。</p>\n<p>表达式  <strong>~0</strong>  可以生成一个全 1 的掩码，不管机器的字大小是多少。</p>\n<h3 id=\"218-c-语言中的逻辑运算\"><a class=\"anchor\" href=\"#218-c-语言中的逻辑运算\">#</a> <strong>2.1.8 C 语言中的逻辑运算</strong></h3>\n<p>逻辑运算符 &amp;&amp; 和 || 如果第一个参数就能确定结果，就不再计算第二个参数</p>\n<h3 id=\"219-c-语言中的移位运算\"><a class=\"anchor\" href=\"#219-c-语言中的移位运算\">#</a> <strong>2.1.9 C 语言中的移位运算</strong></h3>\n<p>左移 k 位丢掉最高的 k 位，并在右端补 k 个 0。</p>\n<p>右移分为<strong>逻辑右移</strong>和<strong>算术右移</strong>。<strong>逻辑右移左端补 0，算术右移左端补最高有效位的值。</strong></p>\n<p>一般都对有符号数使用算术右移，即补符号位的值。无符号数，<strong>只能是逻辑右移</strong>，即补 0</p>\n<h2 id=\"22-整数表示\"><a class=\"anchor\" href=\"#22-整数表示\">#</a> <strong>2.2 整数表示</strong></h2>\n<p>无符号表示与补码表示</p>\n<p>有符号数到无符号数的转换会产生漏洞，<strong>避免错误的方法之一是绝不使用无符号数</strong>。</p>\n<p>除了 C 以外<strong>很少有语言支持无符号整数</strong>，Java 就只支持有符号数</p>\n<h3 id=\"221-整数数据类型\"><a class=\"anchor\" href=\"#221-整数数据类型\">#</a> <strong>2.2.1 整数数据类型</strong></h3>\n<p>在 64 位系统上</p>\n<p>i<strong>nt</strong>：4字节，可表示十进制数字位数：<strong>10位（-20~20亿以内）</strong></p>\n<p><strong>long long</strong>：8字节，可表示十进制数字位数：<strong>19位（千亿亿级）</strong></p>\n<p>long：8字节</p>\n<p><strong>double</strong>：8字节，<strong>精度15位</strong>，可表示十进制数字位数<strong>308位</strong></p>\n<p><strong>float</strong>：4字节，<strong>精度6位</strong>，可表示十进制数字<strong>38位</strong></p>\n<p><strong>char</strong>： <strong>-128~127</strong></p>\n<p>java 只支持有符号数。</p>\n<h3 id=\"222-无符号数的编码\"><a class=\"anchor\" href=\"#222-无符号数的编码\">#</a> <strong>2.2.2 无符号数的编码</strong></h3>\n<p>无符号表示、补码表示与数据的映射都是双射，即一一对应。</p>\n<h3 id=\"223-补码编码\"><a class=\"anchor\" href=\"#223-补码编码\">#</a> <strong>2.2.3 补码编码</strong></h3>\n<p>补码的定义实际就是<strong>将符号位解释为负权</strong>。</p>\n<p>C 库头文件 &lt;limit.h&gt; 定义了一组常量来限定不同整数数据类型的取值范围。INT_MAX、INT_MIN、UINT_MAX</p>\n<p>C 库头文件  <strong>&lt;stdint.h&gt;</strong> 中定义了 uint16_t, int32_t 等类型，用于声明确定宽度类型的整数。</p>\n<h3 id=\"224-有符号数和无符号数之间的转换\"><a class=\"anchor\" href=\"#224-有符号数和无符号数之间的转换\">#</a> <strong>2.2.4 有符号数和无符号数之间的转换</strong></h3>\n<p>在有符号数与无符号数之间进行强制类型转换的结果是<strong>保持位值不变，只改变解释位的方式。</strong></p>\n<p><strong>补码 x 转无符号数</strong></p>\n<p><span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo>≥</mo><mn>0</mn></mrow><annotation encoding=\"application/x-tex\">x \\ge 0</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.7719em;vertical-align:-0.136em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">≥</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">0</span></span></span></span>，值不变</p>\n<p><span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo>&lt;</mo><mn>0</mn></mrow><annotation encoding=\"application/x-tex\">x &lt; 0</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.5782em;vertical-align:-0.0391em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">&lt;</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">0</span></span></span></span>，转换后的值为 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><msup><mn>2</mn><mi>w</mi></msup><mo>+</mo><mi>x</mi></mrow><annotation encoding=\"application/x-tex\">2^w + x</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.7477em;vertical-align:-0.0833em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.6644em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.4306em;\"></span><span class=\"mord mathnormal\">x</span></span></span></span></p>\n<p><strong>无符号数 x 转补码</strong></p>\n<p><span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo>&lt;</mo><msup><mn>2</mn><mrow><mi>w</mi><mo>−</mo><mn>1</mn></mrow></msup></mrow><annotation encoding=\"application/x-tex\">x &lt; 2^{w-1}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.5782em;vertical-align:-0.0391em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">&lt;</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.8141em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8141em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span><span class=\"mbin mtight\">−</span><span class=\"mord mtight\">1</span></span></span></span></span></span></span></span></span></span></span></span>，值不变</p>\n<p><span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo>≥</mo><msup><mn>2</mn><mrow><mi>w</mi><mo>−</mo><mn>1</mn></mrow></msup></mrow><annotation encoding=\"application/x-tex\">x \\ge 2^{w-1}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.7719em;vertical-align:-0.136em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">≥</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.8141em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8141em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span><span class=\"mbin mtight\">−</span><span class=\"mord mtight\">1</span></span></span></span></span></span></span></span></span></span></span></span>，转换后的值为 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo>−</mo><msup><mn>2</mn><mi>w</mi></msup></mrow><annotation encoding=\"application/x-tex\">x - 2^w</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6667em;vertical-align:-0.0833em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6644em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.6644em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span></span></span></span></span></span></span></span></span></span></span></p>\n<h3 id=\"225-c-语言中的有符号数和无符号数\"><a class=\"anchor\" href=\"#225-c-语言中的有符号数和无符号数\">#</a> <strong>2.2.5 C 语言中的有符号数和无符号数</strong></h3>\n<p>C 语言中有符号数和无符号数相加减，有符号被转换成无符号。</p>\n<h3 id=\"226-扩展一个数字的位表示\"><a class=\"anchor\" href=\"#226-扩展一个数字的位表示\">#</a> <strong>2.2.6 扩展一个数字的位表示</strong></h3>\n<p>扩展无符号数使用零扩展，即在最高位前加 0</p>\n<p>扩展有符号数使用符号扩展，即在最高位前加最高有效位的值</p>\n<h3 id=\"227-截断数字\"><a class=\"anchor\" href=\"#227-截断数字\">#</a> <strong>2.2.7 截断数字</strong></h3>\n<p>对一个 w 位的数字截断为一个 k 位数字，将丢弃高 w-k 位。</p>\n<p>对于无符号数而言，截断后的数字实际上等于 w mod 2^k，即取余。</p>\n<h2 id=\"23-整数运算\"><a class=\"anchor\" href=\"#23-整数运算\">#</a> <strong>2.3 整数运算</strong></h2>\n<h3 id=\"231-无符号加法\"><a class=\"anchor\" href=\"#231-无符号加法\">#</a> <strong>2.3.1 无符号加法</strong></h3>\n<p>考虑溢出，C 语言不会将溢出作为错误发出信号</p>\n<p>当 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo>+</mo><mi>y</mi><mo>≥</mo><msup><mn>2</mn><mi>w</mi></msup></mrow><annotation encoding=\"application/x-tex\">x+y \\ge 2^w</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6667em;vertical-align:-0.0833em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.8304em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">y</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">≥</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6644em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.6644em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span></span></span></span></span></span></span></span></span></span></span> 时，实际结果为 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>s</mi><mo>=</mo><mi>x</mi><mo>+</mo><mi>y</mi><mo>−</mo><msup><mn>2</mn><mi>w</mi></msup></mrow><annotation encoding=\"application/x-tex\">s = x+y-2^w</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.4306em;\"></span><span class=\"mord mathnormal\">s</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6667em;vertical-align:-0.0833em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.7778em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">y</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6644em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.6644em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span></span></span></span></span></span></span></span></span></span></span></p>\n<p>对任意的 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo>+</mo><mi>y</mi></mrow><annotation encoding=\"application/x-tex\">x+y</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6667em;vertical-align:-0.0833em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.625em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">y</span></span></span></span>，有 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>s</mi><mo>=</mo><mo stretchy=\"false\">(</mo><mi>x</mi><mo>+</mo><mi>y</mi><mo stretchy=\"false\">)</mo><mtext> </mtext><mo lspace=\"0.22em\" rspace=\"0.22em\"><mrow><mi mathvariant=\"normal\">m</mi><mi mathvariant=\"normal\">o</mi><mi mathvariant=\"normal\">d</mi></mrow></mo><mtext> </mtext><msup><mn>2</mn><mi>w</mi></msup></mrow><annotation encoding=\"application/x-tex\">s = (x+y) \\bmod 2^w</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.4306em;\"></span><span class=\"mord mathnormal\">s</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:1em;vertical-align:-0.25em;\"></span><span class=\"mopen\">(</span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:1em;vertical-align:-0.25em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">y</span><span class=\"mclose\">)</span><span class=\"mspace\" style=\"margin-right:0.0556em;\"></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\"><span class=\"mord\"><span class=\"mord mathrm\">mod</span></span></span><span class=\"mspace\" style=\"margin-right:0.0556em;\"></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6644em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.6644em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span></span></span></span></span></span></span></span></span></span></span></p>\n<p><strong>溢出的结果：</strong> 和小于两个加数</p>\n<p><strong>检验溢出的方式：</strong> 如果 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>s</mi><mo>&lt;</mo><mi>x</mi></mrow><annotation encoding=\"application/x-tex\">s&lt;x</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.5782em;vertical-align:-0.0391em;\"></span><span class=\"mord mathnormal\">s</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">&lt;</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.4306em;\"></span><span class=\"mord mathnormal\">x</span></span></span></span>，说明溢出</p>\n<p><strong>无符号数的非：</strong> <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo>↦</mo><msup><mn>2</mn><mi>w</mi></msup><mo>−</mo><mi>x</mi><mtext> </mtext><mo stretchy=\"false\">(</mo><mi>x</mi><mo>&gt;</mo><mn>0</mn><mo stretchy=\"false\">)</mo></mrow><annotation encoding=\"application/x-tex\">x \\mapsto 2^w - x \\ (x&gt;0)</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.522em;vertical-align:-0.011em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">↦</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.7477em;vertical-align:-0.0833em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.6644em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:1em;vertical-align:-0.25em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\"> </span><span class=\"mopen\">(</span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">&gt;</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:1em;vertical-align:-0.25em;\"></span><span class=\"mord\">0</span><span class=\"mclose\">)</span></span></span></span></p>\n<h3 id=\"232-补码加法\"><a class=\"anchor\" href=\"#232-补码加法\">#</a> <strong>2.3.2 补码加法</strong></h3>\n<p>当 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo>+</mo><mi>y</mi><mo>≥</mo><msup><mn>2</mn><mrow><mi>w</mi><mo>−</mo><mn>1</mn></mrow></msup></mrow><annotation encoding=\"application/x-tex\">x+y \\ge 2^{w-1}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6667em;vertical-align:-0.0833em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.8304em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">y</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">≥</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.8141em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8141em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span><span class=\"mbin mtight\">−</span><span class=\"mord mtight\">1</span></span></span></span></span></span></span></span></span></span></span></span> 时，<span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>s</mi><mo>=</mo><mi>x</mi><mo>+</mo><mi>y</mi><mo>−</mo><msup><mn>2</mn><mi>w</mi></msup></mrow><annotation encoding=\"application/x-tex\">s = x+y-2^w</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.4306em;\"></span><span class=\"mord mathnormal\">s</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6667em;vertical-align:-0.0833em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.7778em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">y</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6644em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.6644em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span></span></span></span></span></span></span></span></span></span></span></p>\n<p>当 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo>+</mo><mi>y</mi><mo>&lt;</mo><mo>−</mo><msup><mn>2</mn><mrow><mi>w</mi><mo>−</mo><mn>1</mn></mrow></msup></mrow><annotation encoding=\"application/x-tex\">x+y &lt; -2^{w-1}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6667em;vertical-align:-0.0833em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.7335em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">y</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">&lt;</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.8974em;vertical-align:-0.0833em;\"></span><span class=\"mord\">−</span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8141em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span><span class=\"mbin mtight\">−</span><span class=\"mord mtight\">1</span></span></span></span></span></span></span></span></span></span></span></span> 时，<span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>s</mi><mo>=</mo><mi>x</mi><mo>+</mo><mi>y</mi><mo>+</mo><msup><mn>2</mn><mi>w</mi></msup></mrow><annotation encoding=\"application/x-tex\">s = x+y+2^w</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.4306em;\"></span><span class=\"mord mathnormal\">s</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6667em;vertical-align:-0.0833em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.7778em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">y</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6644em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.6644em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span></span></span></span></span></span></span></span></span></span></span></p>\n<p><strong>正溢出的结果是负数，负溢出的结果是正数。</strong></p>\n<p><strong>检验溢出的方式：</strong> 当 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo separator=\"true\">,</mo><mi>y</mi><mo>&gt;</mo><mn>0</mn></mrow><annotation encoding=\"application/x-tex\">x,y&gt;0</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.7335em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">y</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">&gt;</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">0</span></span></span></span> 而 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>s</mi><mo>≤</mo><mn>0</mn></mrow><annotation encoding=\"application/x-tex\">s\\le 0</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.7719em;vertical-align:-0.136em;\"></span><span class=\"mord mathnormal\">s</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">≤</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">0</span></span></span></span> 时是正溢出；当 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo separator=\"true\">,</mo><mi>y</mi><mo>&lt;</mo><mn>0</mn></mrow><annotation encoding=\"application/x-tex\">x,y&lt;0</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.7335em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">y</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">&lt;</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">0</span></span></span></span> 而 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>s</mi><mo>≥</mo><mn>0</mn></mrow><annotation encoding=\"application/x-tex\">s\\ge 0</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.7719em;vertical-align:-0.136em;\"></span><span class=\"mord mathnormal\">s</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">≥</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">0</span></span></span></span> 时是负溢出</p>\n<h3 id=\"233-补码的非\"><a class=\"anchor\" href=\"#233-补码的非\">#</a> <strong>2.3.3 补码的非</strong></h3>\n<p>当 x = TMin，-x = TMin；当 x ≠ TMin，-x = -x</p>\n<p><strong>补码非的位级表示：对每一位求补，结果再加 1</strong></p>\n<p><strong>计算补码非的第二种方法：</strong> 假设 k 是最右边的 1 的位置，对 k 左边的所有位取反</p>\n<h3 id=\"234-无符号乘法\"><a class=\"anchor\" href=\"#234-无符号乘法\">#</a> <strong>2.3.4 无符号乘法</strong></h3>\n<p><strong>无符号乘法的积</strong> <strong>m = (x*y) % 2^w</strong></p>\n<h3 id=\"235-补码乘法\"><a class=\"anchor\" href=\"#235-补码乘法\">#</a> <strong>2.3.5 补码乘法</strong></h3>\n<p>可以认为补码乘法和无符号乘法的<strong>位级表示</strong>是一样的</p>\n<p>C语言在运算时将 x,y 视为无符号数进行乘法运算，结果取余后将其按补码方式解释</p>\n<p><strong>补码乘法的积</strong>  <strong>m = (x*y) % 2^w</strong></p>\n<h3 id=\"236-乘以常数\"><a class=\"anchor\" href=\"#236-乘以常数\">#</a> <strong>2.3.6 乘以常数</strong></h3>\n<p>大多数机器上，整数乘法需要  <strong>10 个或更多</strong>的时钟周期，而加法、减法、位级运算和移位只需要 1 个时钟周期</p>\n<p><strong>编译器对整数乘法进行优化的方式</strong>：用<strong>移位和加法或减法</strong>运算的组合来代替常数因子的乘法。</p>\n<p>左移 k 位等于乘以 2^k</p>\n<p>如 x * 14  =  (x&lt;&lt;3)+(x&lt;&lt;2)+(x&lt;&lt;1)   =   (x&lt;&lt;4)-(x&lt;&lt;2)</p>\n<p><strong>判断如何移动的方式</strong>很简单：14 的位级表示为 1110，所以分别左移 3，2，1</p>\n<h3 id=\"237-除以-2-的幂\"><a class=\"anchor\" href=\"#237-除以-2-的幂\">#</a> <strong>2.3.7 除以 2 的幂</strong></h3>\n<p>大多数机器上，整数除法更慢，需要 <strong>30 个或更多</strong>的始终周期。</p>\n<p>（只有）除以 2 的幂可以用移位运算来代替，<strong>无符号采用逻辑右移，补码采用算术右移</strong></p>\n<p>对于有符号数而言，算术右移的结果相当于进行除法运算后<strong>向下舍入</strong></p>\n<p>使用   <strong>(x+(1&lt;&lt;k)-1)&gt;&gt;k</strong>  的结果相当于进行除法运算然后<strong>向零舍入</strong></p>\n<p>代码实现</p>\n<p>(x&lt;0 ? x+(1&lt;&lt;k)-1 : x) &gt;&gt; k;</p>\n<h3 id=\"238-关于整数运算的最后思考\"><a class=\"anchor\" href=\"#238-关于整数运算的最后思考\">#</a> <strong>2.3.8 关于整数运算的最后思考</strong></h3>\n<p><strong>补码使用了与无符号算术运算相同的位级实现</strong>，包括加法、减法、乘法甚至除法。都有完全一样或非常类似的位级行为。</p>\n<h2 id=\"24-浮点数\"><a class=\"anchor\" href=\"#24-浮点数\">#</a> <strong>2.4 浮点数</strong></h2>\n<p>浮点数对于非常大，非常接近零，近似值计算都很有用</p>\n<h3 id=\"241-二进制小数\"><a class=\"anchor\" href=\"#241-二进制小数\">#</a> <strong>2.4.1 二进制小数</strong></h3>\n<p>小数的二进制表示法只能表示那些能够写为 <strong><span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo>×</mo><msup><mn>2</mn><mi>w</mi></msup></mrow><annotation encoding=\"application/x-tex\">x \\times 2^w</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6667em;vertical-align:-0.0833em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">×</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6644em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.6644em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span></span></span></span></span></span></span></span></span></span></span></strong> 的数，<strong>其他的数都是近似表示。</strong> <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi></mrow><annotation encoding=\"application/x-tex\">x</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.4306em;\"></span><span class=\"mord mathnormal\">x</span></span></span></span> 必须可以由形如 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><msup><mn>2</mn><mi>i</mi></msup><mo>+</mo><msup><mn>2</mn><mi>j</mi></msup><mo>+</mo><mo>⋯</mo><mo>+</mo><msup><mn>2</mn><mi>n</mi></msup></mrow><annotation encoding=\"application/x-tex\">2^i + 2^j + \\dots + 2^n</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.908em;vertical-align:-0.0833em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8247em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\">i</span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.908em;vertical-align:-0.0833em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8247em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.05724em;\">j</span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6667em;vertical-align:-0.0833em;\"></span><span class=\"minner\">⋯</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6644em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.6644em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\">n</span></span></span></span></span></span></span></span></span></span></span> 的多项式表示。</p>\n<p>浮点运算的不精确性可能产生严重后果</p>\n<h3 id=\"242-ieee-浮点表示\"><a class=\"anchor\" href=\"#242-ieee-浮点表示\">#</a> <strong>2.4.2 IEEE 浮点表示</strong></h3>\n<p><strong>IEEE 浮点标准</strong>的表示形式为：<span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>V</mi><mo>=</mo><mo stretchy=\"false\">(</mo><mo>−</mo><mn>1</mn><msup><mo stretchy=\"false\">)</mo><mi>S</mi></msup><mo>×</mo><mi>M</mi><mo>×</mo><msup><mn>2</mn><mi>E</mi></msup></mrow><annotation encoding=\"application/x-tex\">V = (-1)^S \\times M \\times 2^E</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6833em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.22222em;\">V</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:1.0913em;vertical-align:-0.25em;\"></span><span class=\"mopen\">(</span><span class=\"mord\">−</span><span class=\"mord\">1</span><span class=\"mclose\"><span class=\"mclose\">)</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8413em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.05764em;\">S</span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">×</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.7667em;vertical-align:-0.0833em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.10903em;\">M</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">×</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.8413em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8413em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.05764em;\">E</span></span></span></span></span></span></span></span></span></span></span>，它分为三部分：</p>\n<p><strong>符号</strong>：<strong>S</strong>  决定是负数还是正数</p>\n<p><strong>阶码</strong>：<strong>E</strong>  的作用是对浮点数加权</p>\n<p><strong>尾数</strong>：<strong>M</strong> 是一个二进制小数。规格化值时范围是 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mn>1</mn><mo>≤</mo><mi>M</mi><mo>&lt;</mo><mn>2</mn></mrow><annotation encoding=\"application/x-tex\">1 \\le M &lt; 2</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.7804em;vertical-align:-0.136em;\"></span><span class=\"mord\">1</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">≤</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.7224em;vertical-align:-0.0391em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.10903em;\">M</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">&lt;</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">2</span></span></span></span>，非规格化值时范围是 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mn>0</mn><mo>≤</mo><mi>M</mi><mo>&lt;</mo><mn>1</mn></mrow><annotation encoding=\"application/x-tex\">0 \\le M &lt; 1</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.7804em;vertical-align:-0.136em;\"></span><span class=\"mord\">0</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">≤</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.7224em;vertical-align:-0.0391em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.10903em;\">M</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">&lt;</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">1</span></span></span></span></p>\n<p><strong>在对浮点数的位编码</strong>时：</p>\n<p>一个单独的符号位编码直接编码 S</p>\n<p>k 位的<strong>阶码字段 e</strong> 编码 E；float 中 k=8，double 中** k=11**</p>\n<p>n 位的<strong>小数字段 f</strong>  编码 M；float 中 n=23，double 中** n=52**</p>\n<p>E 和 M 的编码方式分为<strong>三种情况</strong>：</p>\n<p><strong>规格化的值：</strong> 阶码字段即不全为 0 也不全为 1 时属于规格化值（0001~1110）</p>\n<p>阶码字段解释方式：<span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>E</mi><mo>=</mo><mi>e</mi><mo>−</mo><mo stretchy=\"false\">(</mo><msup><mn>2</mn><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msup><mo>−</mo><mn>1</mn><mo stretchy=\"false\">)</mo></mrow><annotation encoding=\"application/x-tex\">E = e - (2^{k-1}-1)</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6833em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.05764em;\">E</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6667em;vertical-align:-0.0833em;\"></span><span class=\"mord mathnormal\">e</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:1.0991em;vertical-align:-0.25em;\"></span><span class=\"mopen\">(</span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8491em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.03148em;\">k</span><span class=\"mbin mtight\">−</span><span class=\"mord mtight\">1</span></span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:1em;vertical-align:-0.25em;\"></span><span class=\"mord\">1</span><span class=\"mclose\">)</span></span></span></span>；如 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>k</mi><mo>=</mo><mn>4</mn></mrow><annotation encoding=\"application/x-tex\">k=4</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6944em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03148em;\">k</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">4</span></span></span></span> 时，<span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>E</mi></mrow><annotation encoding=\"application/x-tex\">E</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6833em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.05764em;\">E</span></span></span></span> 的范围是 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mo>−</mo><mn>6</mn></mrow><annotation encoding=\"application/x-tex\">-6</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.7278em;vertical-align:-0.0833em;\"></span><span class=\"mord\">−</span><span class=\"mord\">6</span></span></span></span> 到 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mn>7</mn></mrow><annotation encoding=\"application/x-tex\">7</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">7</span></span></span></span>；单精度为 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mo>−</mo><mn>126</mn></mrow><annotation encoding=\"application/x-tex\">-126</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.7278em;vertical-align:-0.0833em;\"></span><span class=\"mord\">−</span><span class=\"mord\">126</span></span></span></span> 到 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mn>127</mn></mrow><annotation encoding=\"application/x-tex\">127</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">127</span></span></span></span></p>\n<p>小数字段解释方式：<strong>M = 1 + f</strong></p>\n<p><strong>非规格化的值</strong>：阶码字段全为 0 时属于非规格化形式</p>\n<p>阶码字段解释方式：<strong>E = 1 - (2^(k-1)-1)</strong> ；<strong>与规格化值中 e = 1 时的 E 相同</strong></p>\n<p>小数字段解释方式：<strong>M = f</strong></p>\n<p><strong>特殊值：</strong> 阶码字段全为 1 时，分两种情况：</p>\n<p><strong>小数字段全为 0：表示无穷</strong></p>\n<p><strong>小数字段非零：表示 NaN。</strong> 比如 ∞-∞ 的结果就返回 NaN</p>\n<h3 id=\"243-数字示例\"><a class=\"anchor\" href=\"#243-数字示例\">#</a> <strong>2.4.3 数字示例</strong></h3>\n<p>0 有 +0.0 和 -0.0 两种表示方式</p>\n<p>最大非规格化数到最小规格化数的过渡是平滑的。</p>\n<p>浮点数能够使用正数排序函数来排序，即浮点数的位级表示当用整数方式来解释时是顺序的（正数升序负数降序）。</p>\n<h2 id=\"问题-21\"><a class=\"anchor\" href=\"#问题-21\">#</a> <strong>问题 2.1</strong></h2>\n<ol>\n<li>十六进制中 A,C,F 分别是多少。</li>\n<li>如何定义有确定字长的整数，在哪个头文件中</li>\n<li>计算机的字长是什么？字长决定了什么？</li>\n<li>short, int, long long 长度分别是多少，long 长度是多少</li>\n<li>一个 int 对象的地址是它所占哪个字节的地址</li>\n<li>多字节类型的小端法和大端法分别是什么，一般计算机是哪种方法</li>\n<li>ASCII 和 Unicode 分别应用于哪些情况？两者的关系是什么</li>\n<li>掩码是什么？如何生成全 1 的数字</li>\n<li>什么是逻辑右移和算术右移，有符号数用哪个？无符号数用哪个</li>\n<li>int, long long,  char，short 可表示的范围分别是多少</li>\n<li>float, double 的精度和可表示范围分别是多少</li>\n<li>补码如何解释位</li>\n<li>有符号数和无符号数的强制类型转换是如何转换的</li>\n<li>有符号转无符号数值如何变化？无符号转有符号数值如何变化？</li>\n<li>如何扩展有符号数？如何扩展无符号数？</li>\n<li>截断一个无符号数从数学运算上如何理解？</li>\n</ol>\n<h3 id=\"回答21\"><a class=\"anchor\" href=\"#回答21\">#</a> <strong>回答2.1</strong></h3>\n<ol>\n<li>A 是 10，C 是 12，F 是 15</li>\n<li>使用 int32_t, int64_t, uint32_t, uint64_t。定义在头文件 &lt;stdint.h&gt; 中</li>\n<li>分别是 2，4，8 位，long 在 32 位程序中是 4 位，在 64 位机器中是 8位</li>\n<li>32 位计算机的字长是 32 位，64 位计算机的字长是 64 位，字长w决定了虚拟空间的大小：&lt;= 2^w。</li>\n<li>最小字节的地址</li>\n<li>小端法将低位表示在低字节，大端法将高位表示在低字节。大部分是小端法。</li>\n<li>ASCII 用于英文文档，UTF-8 用于多语言，所有的 ASCII 字符在 UTF-8 中是一样的</li>\n<li>掩码用来筛选一个数字的某几位，~0 的结果是全 1</li>\n<li>逻辑右移左边补 0，算术右移左端补符号位，有符号数用算术右移，无符号数用逻辑右移</li>\n<li>int 大约是 10 位十进制精度（约 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mo>−</mo><mn>20</mn></mrow><annotation encoding=\"application/x-tex\">-20</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.7278em;vertical-align:-0.0833em;\"></span><span class=\"mord\">−</span><span class=\"mord\">20</span></span></span></span> 亿到 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mn>20</mn></mrow><annotation encoding=\"application/x-tex\">20</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">20</span></span></span></span> 亿）；long long 大约是 19 位；char 为 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mo>−</mo><mn>128</mn></mrow><annotation encoding=\"application/x-tex\">-128</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.7278em;vertical-align:-0.0833em;\"></span><span class=\"mord\">−</span><span class=\"mord\">128</span></span></span></span> 到 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mn>127</mn></mrow><annotation encoding=\"application/x-tex\">127</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">127</span></span></span></span>；short 为 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mo>−</mo><mn>32768</mn></mrow><annotation encoding=\"application/x-tex\">-32768</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.7278em;vertical-align:-0.0833em;\"></span><span class=\"mord\">−</span><span class=\"mord\">32768</span></span></span></span> 到 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mn>32767</mn></mrow><annotation encoding=\"application/x-tex\">32767</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">32767</span></span></span></span></li>\n<li><strong>6位</strong>，10 的 38 次方以内；<strong>15位</strong>，10 的 308 次方以内；</li>\n<li>最高位解释为负权</li>\n<li>位值不变，改变解释的方式</li>\n<li>若 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo>&lt;</mo><mn>0</mn></mrow><annotation encoding=\"application/x-tex\">x&lt;0</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.5782em;vertical-align:-0.0391em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">&lt;</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">0</span></span></span></span>，则 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><msub><mi>u</mi><mi>x</mi></msub><mo>=</mo><mi>x</mi><mo>+</mo><msup><mn>2</mn><mi>w</mi></msup></mrow><annotation encoding=\"application/x-tex\">u_x = x + 2^w</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.5806em;vertical-align:-0.15em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\">u</span><span class=\"msupsub\"><span class=\"vlist-t vlist-t2\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.1514em;\"><span style=\"top:-2.55em;margin-left:0em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\">x</span></span></span></span><span class=\"vlist-s\">​</span></span><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.15em;\"><span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6667em;vertical-align:-0.0833em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6644em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.6644em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span></span></span></span></span></span></span></span></span></span></span>；若无符号数 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><msub><mi>u</mi><mi>x</mi></msub><mo>≥</mo><msup><mn>2</mn><mrow><mi>w</mi><mo>−</mo><mn>1</mn></mrow></msup></mrow><annotation encoding=\"application/x-tex\">u_x \\ge 2^{w-1}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.786em;vertical-align:-0.15em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\">u</span><span class=\"msupsub\"><span class=\"vlist-t vlist-t2\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.1514em;\"><span style=\"top:-2.55em;margin-left:0em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\">x</span></span></span></span><span class=\"vlist-s\">​</span></span><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.15em;\"><span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">≥</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.8141em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8141em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span><span class=\"mbin mtight\">−</span><span class=\"mord mtight\">1</span></span></span></span></span></span></span></span></span></span></span></span>，则 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo>=</mo><msub><mi>u</mi><mi>x</mi></msub><mo>−</mo><msup><mn>2</mn><mi>w</mi></msup></mrow><annotation encoding=\"application/x-tex\">x = u_x - 2^w</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.4306em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.7333em;vertical-align:-0.15em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\">u</span><span class=\"msupsub\"><span class=\"vlist-t vlist-t2\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.1514em;\"><span style=\"top:-2.55em;margin-left:0em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\">x</span></span></span></span><span class=\"vlist-s\">​</span></span><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.15em;\"><span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6644em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.6644em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02691em;\">w</span></span></span></span></span></span></span></span></span></span></span></li>\n<li>左端填充为符号位；左端填充为 0</li>\n<li>对 2^k 取模</li>\n</ol>\n<h2 id=\"问题-22\"><a class=\"anchor\" href=\"#问题-22\">#</a> <strong>问题 2.2</strong></h2>\n<ol>\n<li>考虑溢出，无符号加法的计算公式，无符号乘法的计算公式？如何检验溢出？</li>\n<li>考虑溢出，补码加法的计算公式，补码加法溢出的特点是什么？如何检验溢出？</li>\n<li>考虑溢出，补码乘法的计算方法</li>\n<li>补码的非怎么求？</li>\n<li>整数乘法的计算速度？整数乘法的优化方式？如何判断移动的方式？</li>\n<li>整数除法的计算速度？除以 2 的幂的优化方式？</li>\n<li>整数补码运算与无符号运算的关系</li>\n<li>浮点数表示法的限制</li>\n<li>浮点数位级表示的三个部分？double 分别有多少位？</li>\n<li>阶码和尾数的三种编码方式分别是什么？</li>\n<li>最小的正非规格化值、最大的非规格化值、最小的正规格化值、最大的规格化值的位级表示分别是怎样的？</li>\n<li>什么是向偶数舍入？浮点数的近似匹配采用哪种舍入方式？有什么优点</li>\n<li>浮点运算满足什么运算性质？不满足什么性质？</li>\n<li>从 float 或 double 转换到 int 可能会发生什么情况？</li>\n</ol>\n<h3 id=\"回答22\"><a class=\"anchor\" href=\"#回答22\">#</a> <strong>回答2，2</strong></h3>\n<ol>\n<li>s=(x+y)%2^w；m=(x*y)%2^w；检验加法溢出：s&lt;x 或 s&lt;y</li>\n<li>分三种情况，不溢出，正溢出，负溢出；特点：正溢出的结果是负数，负溢出的结果是正数；检验溢出：结果变号了就是溢出了</li>\n<li>将两个乘数当成无符号数进行乘法计算，截断溢出的部分后，将剩余部分用补码方式解释</li>\n<li>按位求反再加一</li>\n<li>大于等于 10 个时钟周期；用移位+加法/减法优化；根据移位程度 k 的位级表示来判断</li>\n<li>大于等于 30 个时钟周期；逻辑右移或算术右移；算术右移的结果是向下舍入，可以调整为向零舍入</li>\n<li><strong>在位级表示上完全相同或非常相似</strong></li>\n<li>只能表示 x * 2^y 类的数，其他数字只能近似表示</li>\n<li>符号、阶码、尾数；1，11位，52位</li>\n<li>规格化值：阶码非全 0 且非全 1，指数偏移量为 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><msup><mn>2</mn><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msup><mo>−</mo><mn>1</mn></mrow><annotation encoding=\"application/x-tex\">2^{k-1}-1</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.9324em;vertical-align:-0.0833em;\"></span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8491em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.03148em;\">k</span><span class=\"mbin mtight\">−</span><span class=\"mord mtight\">1</span></span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">1</span></span></span></span>，尾数为 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mn>1</mn><mo>+</mo><mi>f</mi></mrow><annotation encoding=\"application/x-tex\">1+f</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.7278em;vertical-align:-0.0833em;\"></span><span class=\"mord\">1</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.8889em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.10764em;\">f</span></span></span></span>；非规格化值：阶码全 0，<span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>E</mi><mo>=</mo><mn>1</mn><mo>−</mo><mo stretchy=\"false\">(</mo><msup><mn>2</mn><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msup><mo>−</mo><mn>1</mn><mo stretchy=\"false\">)</mo></mrow><annotation encoding=\"application/x-tex\">E = 1-(2^{k-1}-1)</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6833em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.05764em;\">E</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.7278em;vertical-align:-0.0833em;\"></span><span class=\"mord\">1</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:1.0991em;vertical-align:-0.25em;\"></span><span class=\"mopen\">(</span><span class=\"mord\"><span class=\"mord\">2</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8491em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.03148em;\">k</span><span class=\"mbin mtight\">−</span><span class=\"mord mtight\">1</span></span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:1em;vertical-align:-0.25em;\"></span><span class=\"mord\">1</span><span class=\"mclose\">)</span></span></span></span>，尾数为 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>f</mi></mrow><annotation encoding=\"application/x-tex\">f</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.8889em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.10764em;\">f</span></span></span></span>；特殊值：阶码全 1，尾数全 0 表示无穷，尾数非全 0 表示 NaN</li>\n<li>末位为1，其他为0；尾数全1，其他全0；阶码末位为1，其他全0；阶码末位和符号位为0，其他全1；</li>\n<li>非中间值向近舍入，中间值向偶数舍入；采用向偶数舍入；优点是统计结果不偏差</li>\n<li>满足交换性和单调性，不满足结合性和分配性</li>\n<li>可能溢出和舍入；如果溢出，转换结果都是 100000，是一个负数。</li>\n</ol>\n",
            "tags": [
                "读书笔记",
                "CSAPP"
            ]
        },
        {
            "id": "https://stefansky07.github.io/2026/05/19/from-report/2026/05/CSAPP%E7%AC%AC%E4%B8%80%E7%AB%A0%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%B3%BB%E7%BB%9F%E6%BC%AB%E6%B8%B8/",
            "url": "https://stefansky07.github.io/2026/05/19/from-report/2026/05/CSAPP%E7%AC%AC%E4%B8%80%E7%AB%A0%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%B3%BB%E7%BB%9F%E6%BC%AB%E6%B8%B8/",
            "title": "CSAPP 第一章 计算机系统漫游",
            "date_published": "2026-05-19T14:56:26.000Z",
            "content_html": "<p><img loading=\"lazy\" src=\"https://raw.githubusercontent.com/Stefansky07/Stefansky07.github.io/hexo-src/source/img/siyuan/1-20260519225738-mcvahzx.jpg\" alt=\"image\" /></p>\n<h1 id=\"csapp-第一章-计算机系统漫游\"><a class=\"anchor\" href=\"#csapp-第一章-计算机系统漫游\">#</a> CSAPP 第一章 计算机系统漫游</h1>\n<h2 id=\"11-信息就是位-上下文\"><a class=\"anchor\" href=\"#11-信息就是位-上下文\">#</a> <strong>1.1</strong>  <strong>信息就是位 + 上下文</strong></h2>\n<p>程序的生命周期从<strong>源程序（源文件）</strong> 开始。源程序实际上就是由 0 和 1 组成的位序列。</p>\n<p>一般用  <strong>ASCII 标准</strong>来表示<strong>文本字符</strong>，实际上是用一个字节的整数值来表示一种字符。</p>\n<p>源文件中每个文本行都是以看不见的 '\\n' 结束的。</p>\n<p>只由 ASCII 字符组成的文件成为<strong>文本文件</strong>，其他都是<strong>二进制文件</strong>。.cpp 文件就是文本文件。</p>\n<p>系统中的所有信息都是由一串比特（bit：位）表示的，区分不同数据对象的<strong>唯一方法</strong>就是根据上下文。</p>\n<p><strong>C 语言的特点</strong></p>\n<ol>\n<li>C 语言小而简单</li>\n<li>C 语言是为了实现 unix 而设计的</li>\n<li>C 语言与 unix 关系密切</li>\n<li>C 语言是系统级编程的首选，也非常适用于应用级程序。</li>\n</ol>\n<h2 id=\"12-程序被其他程序翻译成不同的格式\"><a class=\"anchor\" href=\"#12-程序被其他程序翻译成不同的格式\">#</a> <strong>1.2</strong>  <strong>程序被其他程序翻译成不同的格式</strong></h2>\n<p>从源程序到目标程序要经历<strong>四个步骤：</strong></p>\n<ol>\n<li>源程序被<strong>预处理器</strong>处理得到<strong>修改了的源程序</strong>（文本文件，hello.i）</li>\n<li>再由<strong>编译器</strong>处理得到<strong>汇编程序</strong>（文本文件,hello.s）</li>\n<li>汇编程序由<strong>汇编器</strong>处理得到<strong>可重定位目标程序</strong>（二进制文件,hello.o）</li>\n<li>最后由<strong>链接器</strong>链接得到<strong>可执行目标程序</strong>（二进制文件,hello）</li>\n</ol>\n<p><strong>预处理阶段</strong></p>\n<p>预处理器根据 # 开头的命令修改原始的 c 程序。比如根据 #include&lt;stdio.h&gt; 命令把头文件 stdio.h 的内容直接插入到程序文件中。</p>\n<p><strong>编译阶段</strong></p>\n<p>汇编语言程序中的每条语句都以一种标准的文本格式确切地描述一条低级机器语言指令。</p>\n<p><strong>汇编阶段</strong></p>\n<p>汇编器将hello.s翻译成机器语言指令，把这些指令打包成一种叫可重定向目标程序的格式，并将结果保存至目标文件hello.o中。</p>\n<p><strong>链接阶段</strong></p>\n<p>比如 printf 函数是一个标准 C 库函数，存在于 printf.o 中，这是一个单独的预编译好了的目标文件。链接器将其与汇编得到的二进制文件合并得到可执行目标文件。</p>\n<h2 id=\"13-了解编译系统如何工作是大有用处的\"><a class=\"anchor\" href=\"#13-了解编译系统如何工作是大有用处的\">#</a> <strong>1.3 了解编译系统如何工作是大有用处的</strong></h2>\n<p>用处：</p>\n<ul>\n<li>优化程序性能</li>\n<li>理解链接时出现的错误</li>\n<li>避免安全漏洞</li>\n</ul>\n<h2 id=\"14-处理器读并解释储存在内存中的指令\"><a class=\"anchor\" href=\"#14-处理器读并解释储存在内存中的指令\">#</a> <strong>1.4 处理器读并解释储存在内存中的指令</strong></h2>\n<p>shell 是一个命令行解释器，它输出一个提示符（&gt;&gt;），等待输入一个命令行，然后执行命令。如果输入的是可执行文件的名字，就运行该文件。</p>\n<h3 id=\"141-系统的硬件组成\"><a class=\"anchor\" href=\"#141-系统的硬件组成\">#</a> <strong>1.4.1 系统的硬件组成</strong></h3>\n<p>主要包括<strong>总线、I/O 设备、处理器、主存储器四个部分</strong></p>\n<p><strong>总线</strong></p>\n<p>总线一次可以传输一个定长的字节块，称为字。64位系统即总线一次可以传输 64 位（8字节），这里一个字就是 8 字节</p>\n<p><strong>I/O 设备</strong></p>\n<p>每个 I/O 设备通过一个<strong>控制器</strong>或<strong>适配器</strong>与 I/O 总线相连。</p>\n<p><strong>控制器</strong>是 I/O 设备本身或主板上的芯片组，<strong>适配器</strong>则是一块插在主板上的卡。</p>\n<p><strong>主存</strong></p>\n<p>主存是由一组<strong>动态随机存取内存（DRAM）</strong> 组成的。</p>\n<p>从逻辑上看，存储器是一个线性的字节数组，每个字节都有唯一的地址。</p>\n<p><strong>处理器</strong></p>\n<p>处理器是<strong>解释存储在主存中指令</strong>的引擎。</p>\n<p>处理器的<strong>核心</strong>是一个<strong>程序计数器（PC)</strong></p>\n<p>程序计数器是一个大小为<strong>一个字</strong>的存储设备，存储CPU即将执行的<strong>下一条指令的地址</strong>。</p>\n<p>处理器就是在不断执行程序计数器指向的指令。每执行一条，程序计数器更新一次，指向下一条指令。</p>\n<p>处理器会按照<strong>指令执行模型（指令集架构）</strong> 解释指令中的位并执行相应操作。</p>\n<p>每条指令的操作是围绕<strong>主存、寄存器文件、算数/逻辑单元（ALU）</strong> 进行的。</p>\n<p><strong>寄存器文件</strong>：单个字长，有唯一的名字。</p>\n<p><strong>ALU：</strong> 计算新的数据和地址值。</p>\n<p>几个简单指令的操作：</p>\n<p><strong>加载</strong>：从主存复制一个字或字节到寄存器，覆盖原来内容</p>\n<p><strong>存储</strong>：从寄存器复制一个字或字节到主存，覆盖原来内容</p>\n<p><strong>操作</strong>：把两个寄存器的内容复制到 ALU，ALU 对这两个字做算术运算，并把结果存到一个寄存器中</p>\n<p><strong>跳转</strong>：从指令中抽取一个字复制到程序计数器中，覆盖原来内容。</p>\n<p>区分处理器指令集架构和微体系架构：</p>\n<p><strong>指令集架构：</strong> 每条机器指令的效果</p>\n<p><strong>微体系架构：</strong> 处理器实际上是如何实现的</p>\n<h3 id=\"142-运行-hello-程序\"><a class=\"anchor\" href=\"#142-运行-hello-程序\">#</a> <strong>1.4.2 运行 hello 程序</strong></h3>\n<p>执行目标文件时，shell 程序将位于磁盘目标文件中的字符逐个读入寄存器，然后放到主存中。之后处理器就开始执行目标文件的机器语言指令，从 main 程序开始。</p>\n<p>利用<strong>直接存储器存取（DMA）</strong> 可以不通过寄存器，直接将数据从磁盘到达内存。</p>\n<p>以输出打印 hello world 为例，处理器将 hello world 的字节复制到寄存器，然后再复制到显示器，最后显示在屏幕上。</p>\n<p><strong>整个流程：读取文件字符到寄存器 → 存储到主存  → 执行指令</strong> →  <strong>加载 helloworld 到寄存器 → 复制到显示器 → 显示</strong></p>\n<h2 id=\"15-高速缓存至关重要\"><a class=\"anchor\" href=\"#15-高速缓存至关重要\">#</a> <strong>1.5 高速缓存至关重要</strong></h2>\n<p>从主存读取一个字比磁盘快 <strong>1000 万</strong>倍。</p>\n<p>从寄存器文件读取比主存块 <strong>100 倍</strong>，并且差距还在加大。</p>\n<p>高速缓存（cache）用来解决处理器与主存间的差异。</p>\n<p><strong>L1 高速缓存</strong>位于  <strong>CPU</strong>  上，容量为数万字节（<strong>几十 MB</strong>）。L1 比 L2 快  <strong>5</strong>  倍。</p>\n<p><strong>L2 高速缓存</strong>通过<strong>一条特殊的总线</strong>与 CPU 连接，容量为数十万到数百万字节（<strong>几百 MB 到 几 GB</strong>）。L2 比 主存快 <strong>5~10</strong> 倍</p>\n<p>新的系统还有 L3。</p>\n<p>通过让高速缓存里存放可能经常访问的数据，让大部分的内存操作都在高速缓存中完成。</p>\n<h2 id=\"16-存储设备形成层次结构\"><a class=\"anchor\" href=\"#16-存储设备形成层次结构\">#</a> <strong>1.6 存储设备形成层次结构</strong></h2>\n<p>存储器层次结构共 7 层，<strong>主要思想</strong>是上一层的存储器作为低一层的高速缓存。</p>\n<p>从上到下，容量更大，运行更慢，每字节价格更便宜。</p>\n<p>0层：寄存器</p>\n<p>1层：L1高速缓存(SRAM)</p>\n<p>2层：L2高速缓存(SRAM)</p>\n<p>3层：L3高速缓存（SRAM）</p>\n<p>4层：主存（DRAM）</p>\n<p>5层：本地二级存储（本地磁盘）</p>\n<p>6层：远程二级存储（分布式文件系统，Web服务器）</p>\n<h2 id=\"17-操作系统管理硬件\"><a class=\"anchor\" href=\"#17-操作系统管理硬件\">#</a> <strong>1.7 操作系统管理硬件</strong></h2>\n<p>操作系统的<strong>两个基本功能：</strong></p>\n<ul>\n<li>防止硬件被失控的应用程序滥用</li>\n<li>向应用程序提供简单一致的机制来控制复杂的低级硬件设备</li>\n</ul>\n<p>操作系统所应用的<strong>三个基本的抽象概念：</strong></p>\n<ul>\n<li><strong>进程</strong>：对处理器、主存和 I/O 设备的抽象表示</li>\n<li><strong>虚拟内存</strong>：对主存和磁盘的抽象表示</li>\n<li><strong>文件</strong>：对 I/O 设备的抽象表示</li>\n</ul>\n<h3 id=\"171-进程\"><a class=\"anchor\" href=\"#171-进程\">#</a> <strong>1.7.1 进程</strong></h3>\n<p><strong>进程：</strong> 对操作系统正在运行的程序的一种抽象。</p>\n<p><strong>并发运行：</strong> 一个进程的指令和另一个进程的指令是交错执行的。</p>\n<p>一个系统可以同时运行多个进程，实际上这些进程是并发运行的。</p>\n<p>操作系统通过<strong>上下文切换</strong>来实现并发运行。上下文是跟踪进程运行所需的所有<strong>状态信息</strong>，可能存在于PC、寄存器文件、主存等地方。</p>\n<p><strong>任何时刻，单处理器只能执行一个进程的代码。</strong></p>\n<p>操作系统<strong>内核</strong>是操作系统代码常驻主存的部分，从一个进程到另一个进程的转换是由内核管理的。</p>\n<p>内核不是一个独立的进程，是一系列代码和数据结构的集合。</p>\n<p>当应用程序需要操作系统的某些操作时，就把控制权传递给内核，内核执行完操作后返回应用程序。</p>\n<h3 id=\"172-线程\"><a class=\"anchor\" href=\"#172-线程\">#</a> <strong>1.7.2 线程</strong></h3>\n<p><strong>一个进程由多个线程组成</strong>，每个线程都运行在进程的上下文中，共享同样的代码和全局数据。</p>\n<p>多线程之间比多进程之间更容易共享数据，且线程一般来说比进程更高效。</p>\n<h3 id=\"173-虚拟内存\"><a class=\"anchor\" href=\"#173-虚拟内存\">#</a> <strong>1.7.3 虚拟内存</strong></h3>\n<p>机器级程序将内存视为一个庞大的字节数组，称为<strong>虚拟内存</strong>。</p>\n<p>内存的每个字节由地址来标识，所有可能地址的集合就是<strong>虚拟地址空间。</strong></p>\n<p>虚拟内存使每个进程都以为自己独占了主存。每个进程看到的内存都是<strong>一致的</strong>，即<strong>虚拟地址空间</strong>。</p>\n<p>在linux中，每个进程看到的虚拟地址空间由以下几个部分组成：</p>\n<p><strong>程序代码和数据</strong></p>\n<p><strong>堆（运行时堆）</strong></p>\n<p><strong>共享库</strong></p>\n<p><strong>栈（用户栈）</strong></p>\n<p><strong>内核虚拟内存</strong></p>\n<p>地址从低到高，最高层的<strong>内核虚拟内存</strong>保存的是操作系统中的代码和数据，这部分每个进程都一样。</p>\n<p><strong>程序代码和数据</strong></p>\n<p>对所有进程来说，代码都是从同一个固定地址开始，紧接着是与全局变量对应的数据区。代码和数据区都是按照可执行文件的内容初始化的。代码和数据区在进程开始运行时就被指定了大小。</p>\n<p><strong>堆</strong></p>\n<p>而运行时堆是根据 malloc 和 free 函数的调用在运行时<strong>动态地</strong>扩展和收缩的。</p>\n<p><strong>共享库</strong></p>\n<p>地址空间的中间部分用来存放共享库的代码和数据。如 C 标准库、数学库等都属于共享库</p>\n<p><strong>栈</strong></p>\n<p>用户栈和堆一样，在程序执行期间可以<strong>动态的扩展和收缩</strong>，编译器用它来<strong>实现函数调用</strong>。当调用函数时，栈增长，从函数返回时，栈收缩</p>\n<h3 id=\"174-文件\"><a class=\"anchor\" href=\"#174-文件\">#</a> <strong>1.7.4 文件</strong></h3>\n<p>文件就是<strong>字节序列</strong>，仅此而已。</p>\n<p>每个 I/O 设备，包括磁盘、键盘、显示器、网络，都可以看成是文件。</p>\n<h2 id=\"18-系统之间利用网络通信\"><a class=\"anchor\" href=\"#18-系统之间利用网络通信\">#</a> <strong>1.8 系统之间利用网络通信</strong></h2>\n<p>从一个单独的系统而言，网络可以视为一个 I/O 设备。</p>\n<p>以在一个远端服务器运行程序为例，在本地输入，在远端执行，执行结果发送回本地输出。</p>\n<h2 id=\"19-重要主题\"><a class=\"anchor\" href=\"#19-重要主题\">#</a> <strong>1.9 重要主题</strong></h2>\n<h3 id=\"191-amdahl-定律\"><a class=\"anchor\" href=\"#191-amdahl-定律\">#</a> <strong>1.9.1 Amdahl 定律</strong></h3>\n<p>Amdahl 定律的主要观点：要加速整个系统，必须提升全系统中相当大的部分。</p>\n<h3 id=\"192-并发和并行\"><a class=\"anchor\" href=\"#192-并发和并行\">#</a> <strong>1.9.2 并发和并行</strong></h3>\n<p>区分并发与并行：</p>\n<p><strong>并发：</strong> 一个通用的概念，指一个同时具有多个活动的系统</p>\n<p><strong>并行：</strong> 用并发来使系统运行得更快</p>\n<p>并行可以在多个抽象层次上运用。从高到低有以下三个层次</p>\n<p><strong>1.线程级并行</strong></p>\n<p>传统意义上的并发执行是通过单处理器在进程间快速切换<strong>模拟</strong>出来的。</p>\n<p>多处理器系统由一个操作系统控制多个 CPU。结构如下</p>\n<p><img loading=\"lazy\" src=\"https://raw.githubusercontent.com/Stefansky07/Stefansky07.github.io/hexo-src/source/img/siyuan/wps1-20250309124757-ilcw3gx.jpg\" alt=\"\" /></p>\n<p>L1 高速缓存被分为两个部分：一个保存最近取到的指令，一个存放数据。</p>\n<p><strong>超线程</strong>又称<strong>同时多线程</strong>，它允许一个 CPU 执行多个控制流。 CPU 有的硬件有多个备份，比如程序计数器和寄存器文件，而其他硬件只有一份，比如浮点算术运算单元。常规 CPU 需要约 <strong>20000</strong>  个时钟周期来切换线程，超线程 CPU 可以在<strong>单个周期</strong>的基础上切换线程，比如一个线程在等待数据装在到高速缓存，CPU 就可以去执行另一个线程。</p>\n<p>i7 处理器每个核执行两个线程，所以是 <strong>4 核 8 线程</strong>，8 个线程都并行执行。</p>\n<p><strong>2. 指令级并行</strong></p>\n<p>每条指令从开始到结束一般需要 20 个或更多的时钟周期，通过指令级并行，可以实现每个周期 2~4 条指令的执行速率。</p>\n<p>如果比一个周期一条指令更快，就称为<strong>超标量处理器</strong>，现在一般都是超标量。</p>\n<p><strong>3. 单指令、多数据并行</strong></p>\n<p>在最低层次上，现代处理器允许一条指令产生多个可以并行执行的操作，称为单指令、多数据并行，即 SIMD 并行。</p>\n<h3 id=\"193-计算机系统中抽象的重要性\"><a class=\"anchor\" href=\"#193-计算机系统中抽象的重要性\">#</a> <strong>1.9.3 计算机系统中抽象的重要性</strong></h3>\n<p><img loading=\"lazy\" src=\"https://raw.githubusercontent.com/Stefansky07/Stefansky07.github.io/hexo-src/source/img/siyuan/wps2-20250309124757-ze2t6mw.jpg\" alt=\"\" /></p>\n<p><strong>指令集架构</strong>是对 CPU 硬件的抽象，使用这个抽象，CPU 看起来好像一次只执行机器代码程序的一条指令，实际上底层硬件并行地执行多条指令。</p>\n<p><strong>虚拟机</strong>是对整个计算机系统的抽象，包括操作系统、处理器和程序。</p>\n<h2 id=\"问题11\"><a class=\"anchor\" href=\"#问题11\">#</a> <strong>问题1.1</strong></h2>\n<ol>\n<li>信息的两个要素</li>\n<li>文本文件是由什么组成的</li>\n<li>从源程序到目标程序的四个阶段</li>\n<li>系统硬件组成的四个部分</li>\n<li>从逻辑上看主存是什么样的</li>\n<li>主存硬件上是什么组成的</li>\n<li>处理器的核心是什么？它主要干什么</li>\n<li>用最简单的方式描述处理器运行的过程</li>\n<li>指令操作围绕哪三个部件执行</li>\n<li>四个指令操作：加载、存储、操作、跳转分别是什么</li>\n<li>指令集架构和微体系架构分别是什么</li>\n<li>CPU 执行一个目标文件的流程</li>\n<li>从源程序到得出运行结果的整个流程</li>\n</ol>\n<h3 id=\"回答11\"><a class=\"anchor\" href=\"#回答11\">#</a> <strong>回答1.1</strong></h3>\n<ol>\n<li>位+上下文</li>\n<li>由 ASCII 标准表示的字符</li>\n<li>预处理器，编译器，汇编器，链接器</li>\n<li>处理器、主存、I/O 设备、总线</li>\n<li>一串线性字节数组，每个字节有唯一的地址</li>\n<li>动态随机存取内存（DRAM）</li>\n<li>程序计数器，一个字，存储下一条指令的地址。</li>\n<li>执行 PC 所指向的指令，然后更新 PC。</li>\n<li>主存、寄存器和 ALU，寄存器存放一些指令和数据、状态等，ALU 计算数据和地址</li>\n<li>略</li>\n<li>一个描述指令的效果，一个是硬件实现方式</li>\n<li>把文件从磁盘读取到寄存器，再放到主存（直接存取可以实现不经过寄存器），然后从主存读取指令开始执行，如果要显示一个数字，把数字读到寄存器，再输出到显示器</li>\n<li>源程序→目标程序→处理器执行目标程序</li>\n</ol>\n<h2 id=\"问题12\"><a class=\"anchor\" href=\"#问题12\">#</a> <strong>问题1.2</strong></h2>\n<ol>\n<li>主存比磁盘快多少，寄存器比主存快多少</li>\n<li>L1 cache 位于哪里？有多大？有多快？L2 呢？</li>\n<li>高速缓存是怎么用的？</li>\n<li>存储器层次结构有几层，主要思想是什么？</li>\n<li>操作系统的三个基本抽象概念是什么</li>\n<li>什么是进程？进程有什么特点</li>\n<li>什么是操作系统内核？主要做什么</li>\n<li>什么是虚拟地址空间？由几个部分组成</li>\n<li>并发和并行的区别，和顺序执行的区别</li>\n<li>什么是线程级并行</li>\n<li>什么是超线程</li>\n<li>什么是指令级并行</li>\n<li>什么是单指令、多数据并行</li>\n<li>什么是指令集架构</li>\n</ol>\n<h3 id=\"回答12\"><a class=\"anchor\" href=\"#回答12\">#</a> <strong>回答1.2</strong></h3>\n<ol>\n<li>千万倍，百倍。这表明如果需要从磁盘取指令来执行是慢的可怕的。</li>\n<li>CPU上，几十MB，比 L2 快 5 倍；L2 通过特殊的总线与 CPU 相连，几百 MB&lt;sub&gt;几 GB，比主存快 5&lt;/sub&gt;10 倍</li>\n<li>把可能经常访问的数据放到高速缓存中</li>\n<li>7层，用上一层做下一层的高速缓存</li>\n<li>进程、虚拟内存、文件</li>\n<li>系统正在执行的程序，系统在某一时刻只能执行一个进程</li>\n<li>操作系统常驻主存的部分，负责管理进程的切换，方式是上下文切换。</li>\n<li>进程看到的是一个虚拟的地址空间。从低到高依次是程序代码和数据、运行时堆、共享库、用户栈、内核虚拟内存</li>\n<li>并发是交错执行，并行是同时执行，顺序是执行完一个再执行另一个</li>\n<li>四个核一个核一个线程就是四线程并行，</li>\n<li>超线程可以实现一个核两个线程。因为如PC、寄存器等硬件有多个备份，可以在一个线程需要等待拷贝的时候执行另一个线程。相当于充分利用硬件交错执行不同线程。</li>\n<li>一个指令需要20多个时钟周期，并行后一秒可以执行2~4个指令</li>\n<li>一个指令的几个简单操作并行执行（硬件层面）</li>\n<li>一种抽象，看起来好像是指令顺序依次执行，实际上背地里是指令级并行。</li>\n</ol>\n",
            "tags": []
        },
        {
            "id": "https://stefansky07.github.io/post/heap-sort-zwnlhn.html",
            "url": "https://stefansky07.github.io/post/heap-sort-zwnlhn.html",
            "title": "堆排序",
            "date_published": "2026-05-19T14:38:26.000Z",
            "content_html": "<p><img loading=\"lazy\" src=\"assets/12e6d14e0a9c02662c503fb81a53406-20260520222956-omzwou2.jpg\" alt=\"image\" /></p>\n<h1 id=\"堆排序\"><a class=\"anchor\" href=\"#堆排序\">#</a> 堆排序</h1>\n<h1 id=\"一-堆的知识点笔记\"><a class=\"anchor\" href=\"#一-堆的知识点笔记\">#</a> 一、堆的知识点笔记</h1>\n<h2 id=\"1-堆的基本概念\"><a class=\"anchor\" href=\"#1-堆的基本概念\">#</a> 1. 堆的基本概念</h2>\n<p>堆是一种特殊的完全二叉树，通常用数组顺序存储。</p>\n<p>堆有两个核心要求：</p>\n<ol>\n<li>结构上必须是完全二叉树。</li>\n<li>数值上要满足堆的大小关系。</li>\n</ol>\n<p>大根堆要求：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>父节点 >= 子节点</span></span></code></pre>\n<p>小根堆要求：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>父节点 &#x3C;= 子节点</span></span></code></pre>\n<p>大根堆的堆顶是最大值。<br />\n小根堆的堆顶是最小值。</p>\n<hr />\n<h2 id=\"2-堆的数组下标关系\"><a class=\"anchor\" href=\"#2-堆的数组下标关系\">#</a> 2. 堆的数组下标关系</h2>\n<p>如果数组下标从 1 开始：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>父节点 i</span></span>\n<span class=\"line\"><span>左孩子 2i</span></span>\n<span class=\"line\"><span>右孩子 2i + 1</span></span></code></pre>\n<p>如果数组下标从 0 开始：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>父节点 (i - 1) / 2</span></span>\n<span class=\"line\"><span>左孩子 2i + 1</span></span>\n<span class=\"line\"><span>右孩子 2i + 2</span></span></code></pre>\n<p>考试中很多堆题默认下标从 1 开始。</p>\n<hr />\n<h2 id=\"3-堆的插入操作\"><a class=\"anchor\" href=\"#3-堆的插入操作\">#</a> 3. 堆的插入操作</h2>\n<p>堆插入新元素时，不能指定位置插入。</p>\n<p>标准流程是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>1. 新元素先放到堆的末尾</span></span>\n<span class=\"line\"><span>2. 和父节点比较</span></span>\n<span class=\"line\"><span>3. 如果不满足堆的性质，就向上调整</span></span></code></pre>\n<p>以大根堆为例：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>如果新元素 > 父节点，就交换</span></span>\n<span class=\"line\"><span>如果新元素 &#x3C;= 父节点，就停止</span></span></code></pre>\n<p>以小根堆为例：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>如果新元素 &#x3C; 父节点，就交换</span></span>\n<span class=\"line\"><span>如果新元素 >= 父节点，就停止</span></span></code></pre>\n<p>插入操作的调整方向是​<strong>向上调整</strong>。</p>\n<p>时间复杂度：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>O(log n)</span></span></code></pre>\n<p>易错点：</p>\n<p><strong>最后一次判断“不需要交换”的比较也要算入关键字比较次数。</strong></p>\n<hr />\n<h2 id=\"4-堆的删除操作\"><a class=\"anchor\" href=\"#4-堆的删除操作\">#</a> 4. 堆的删除操作</h2>\n<p>堆最常见的删除操作是删除堆顶元素。</p>\n<p>大根堆删除堆顶，就是删除最大值。<br />\n小根堆删除堆顶，就是删除最小值。</p>\n<p>标准流程是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>1. 删除堆顶元素</span></span>\n<span class=\"line\"><span>2. 用最后一个元素补到堆顶</span></span>\n<span class=\"line\"><span>3. 从堆顶开始向下调整</span></span></code></pre>\n<p>为什么用最后一个元素补堆顶？</p>\n<p>因为堆必须保持完全二叉树结构。<br />\n最后一个元素是最右下角的叶子结点，删除它不会破坏完全二叉树。</p>\n<p>如果用堆顶的子结点补上去，中间会留下空洞，还要继续补空位，过程复杂，也不符合标准堆删除操作。</p>\n<hr />\n<h2 id=\"5-删除后的向下调整\"><a class=\"anchor\" href=\"#5-删除后的向下调整\">#</a> 5. 删除后的向下调整</h2>\n<p>以大根堆为例，向下调整时：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>1. 先比较左右孩子，选较大的孩子</span></span>\n<span class=\"line\"><span>2. 再让当前元素和较大的孩子比较</span></span>\n<span class=\"line\"><span>3. 如果当前元素小，就交换</span></span>\n<span class=\"line\"><span>4. 继续向下检查</span></span></code></pre>\n<p>以小根堆为例，向下调整时：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>1. 先比较左右孩子，选较小的孩子</span></span>\n<span class=\"line\"><span>2. 再让当前元素和较小的孩子比较</span></span>\n<span class=\"line\"><span>3. 如果当前元素大，就交换</span></span>\n<span class=\"line\"><span>4. 继续向下检查</span></span></code></pre>\n<p>删除操作的调整方向通常是​<strong>向下调整</strong>。</p>\n<p>时间复杂度：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>O(log n)</span></span></code></pre>\n<p>易错点：</p>\n<p><strong>向下调整中，最后一次判断“不需要交换”的比较也要算入关键字比较次数。</strong></p>\n<hr />\n<h2 id=\"6-大根堆能不能删除特定元素\"><a class=\"anchor\" href=\"#6-大根堆能不能删除特定元素\">#</a> 6. 大根堆能不能删除特定元素</h2>\n<p>可以删除，但不擅长。</p>\n<p>如果只知道元素值，不知道位置，需要先查找该元素。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>查找：O(n)</span></span>\n<span class=\"line\"><span>调整：O(log n)</span></span>\n<span class=\"line\"><span>整体：O(n)</span></span></code></pre>\n<p>如果已经知道该元素的位置，就可以：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>1. 用最后一个元素替换该位置</span></span>\n<span class=\"line\"><span>2. 根据情况向上或向下调整</span></span></code></pre>\n<p>如果替换后的元素比父节点大，大根堆中要向上调整。<br />\n如果替换后的元素比孩子小，大根堆中要向下调整。</p>\n<p>所以准确说：</p>\n<p><strong>堆不是不能删除特定元素，而是不擅长快速查找并删除特定元素。</strong></p>\n<hr />\n<h2 id=\"7-堆是否支持插入到特定位置\"><a class=\"anchor\" href=\"#7-堆是否支持插入到特定位置\">#</a> 7. 堆是否支持插入到特定位置</h2>\n<p>一般不支持。</p>\n<p>堆的插入位置由完全二叉树结构决定，新元素必须先插入到最后一个位置。</p>\n<p>堆不是按位置组织数据的，而是按优先级组织数据的。</p>\n<p>所以堆关心的是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>堆顶最大或最小</span></span>\n<span class=\"line\"><span>父子之间满足大小关系</span></span>\n<span class=\"line\"><span>整体保持完全二叉树</span></span></code></pre>\n<p>它不关心某个元素必须放在哪个具体位置。</p>\n<hr />\n<h2 id=\"8-建堆操作\"><a class=\"anchor\" href=\"#8-建堆操作\">#</a> 8. 建堆操作</h2>\n<p>建堆不是从第一个元素开始调，而是从最后一个非叶子结点开始调。</p>\n<p>如果有 n 个元素，最后一个非叶子结点是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>n / 2</span></span></code></pre>\n<p>从这个位置开始，依次向前调整：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>n/2, n/2 - 1, ..., 1</span></span></code></pre>\n<p>建堆的时间复杂度是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>O(n)</span></span></code></pre>\n<p>注意：</p>\n<p>如果一个一个插入来建堆，复杂度是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>O(nlog n)</span></span></code></pre>\n<p>但堆排序中的建初始堆是自底向上调整，所以是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>O(n)</span></span></code></pre>\n<hr />\n<h2 id=\"9-堆排序\"><a class=\"anchor\" href=\"#9-堆排序\">#</a> 9. 堆排序</h2>\n<p>堆排序的基本流程是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>1. 建初始堆</span></span>\n<span class=\"line\"><span>2. 输出堆顶元素</span></span>\n<span class=\"line\"><span>3. 用最后一个元素补堆顶</span></span>\n<span class=\"line\"><span>4. 重新调整堆</span></span>\n<span class=\"line\"><span>5. 重复直到排序完成</span></span></code></pre>\n<p>如果要从小到大排序，通常使用大根堆。<br />\n每次把最大值放到数组末尾。</p>\n<p>堆排序时间复杂度：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>最好：O(nlog n)</span></span>\n<span class=\"line\"><span>平均：O(nlog n)</span></span>\n<span class=\"line\"><span>最坏：O(nlog n)</span></span></code></pre>\n<p>空间复杂度：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>O(1)</span></span></code></pre>\n<p>稳定性：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>不稳定</span></span></code></pre>\n<p>原因是堆排序中会发生远距离交换，相同关键字的相对顺序可能被改变。</p>\n<hr />\n<h2 id=\"10-堆与-top-k-问题\"><a class=\"anchor\" href=\"#10-堆与-top-k-问题\">#</a> 10. 堆与 Top-K 问题</h2>\n<p>堆非常适合处理海量数据中的前 k 个数问题。</p>\n<p>如果要选最大的 k 个数，用小根堆。</p>\n<p>原因是：</p>\n<p>小根堆堆顶是当前 k 个候选数中最小的数。<br />\n如果新数比堆顶还小，就没资格进入最大 k 个数。<br />\n如果新数比堆顶大，就替换堆顶，然后调整小根堆。</p>\n<p>如果要选最小的 k 个数，用大根堆。</p>\n<p>原因是：</p>\n<p>大根堆堆顶是当前 k 个候选数中最大的数。<br />\n如果新数比堆顶还大，就没资格进入最小 k 个数。<br />\n如果新数比堆顶小，就替换堆顶，然后调整大根堆。</p>\n<p>记忆：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>选最大 k 个数，用小根堆</span></span>\n<span class=\"line\"><span>选最小 k 个数，用大根堆</span></span></code></pre>\n<p>堆顶保存的是当前结果中最容易被淘汰的那个元素。</p>\n<hr />\n<h1 id=\"二-做题整理\"><a class=\"anchor\" href=\"#二-做题整理\">#</a> 二、做题整理</h1>\n<h2 id=\"第-2-题\"><a class=\"anchor\" href=\"#第-2-题\">#</a> 第 2 题</h2>\n<p><strong>题目文字版</strong></p>\n<p>简单选择排序算法的比较次数和移动次数分别为（ ）。</p>\n<p>A. <code>O(n)，O(log₂n)</code>​<br />\nB. <code>O(log₂n)，O(n²)</code>​<br />\nC. <code>O(n²)，O(n)</code>​<br />\nD. <code>O(nlog₂n)，O(n)</code></p>\n<p><strong>答案</strong></p>\n<p>C</p>\n<blockquote>\n<p>[!NOTE]<br />\n<strong>解析</strong></p>\n<p>简单选择排序每一趟都要在未排序部分中找最小值。</p>\n<p>比较次数为：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>(n - 1) + (n - 2) + ... + 1 = O(n²)</span></span></code></pre>\n<p>但是每一趟最多只交换一次，所以移动次数是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>O(n)</span></span></code></pre>\n<p>所以答案是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>比较次数 O(n²)，移动次数 O(n)</span></span></code></pre>\n</blockquote>\n<hr />\n<h2 id=\"第-5-题\"><a class=\"anchor\" href=\"#第-5-题\">#</a> 第 5 题</h2>\n<p><strong>题目文字版</strong></p>\n<p>在含有 <code>n</code>​ 个元素的小根堆中，下标从 <code>1</code> 开始，关键字最大的元素可能存储在（ ）位置。</p>\n<p>A. <code>n/2</code>​<br />\nB. <code>n/2 + 2</code>​<br />\nC. <code>1</code>​<br />\nD. <code>n/2 - 1</code></p>\n<p><strong>答案</strong></p>\n<p>B</p>\n<blockquote>\n<p>[!NOTE]<br />\n<strong>解析</strong></p>\n<p>小根堆满足：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>父节点 &#x3C;= 子节点</span></span></code></pre>\n<p>所以堆顶一定是最小值。</p>\n<p>关键字最大的元素一般只能出现在叶子结点中。</p>\n<p>下标从 <code>1</code> 开始时：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>1 到 n/2 是非叶子结点</span></span>\n<span class=\"line\"><span>n/2 + 1 到 n 是叶子结点</span></span></code></pre>\n<p>四个选项中，只有 <code>n/2 + 2</code> 属于叶子结点区域。</p>\n<p>所以选：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>B</span></span></code></pre>\n</blockquote>\n<hr />\n<h2 id=\"第-7-题\"><a class=\"anchor\" href=\"#第-7-题\">#</a> 第 7 题</h2>\n<p><strong>题目文字版</strong></p>\n<p>构建 <code>n</code>​ 个记录的初始堆，其时间复杂度为（ ）；对 <code>n</code> 个记录进行堆排序，最坏情况下其时间复杂度为（ ）。</p>\n<p>A. <code>O(n)</code>​<br />\nB. <code>O(n²)</code>​<br />\nC. <code>O(log₂n)</code>​<br />\nD. <code>O(nlog₂n)</code></p>\n<p><strong>答案</strong></p>\n<p>A，D</p>\n<blockquote>\n<p>[!NOTE]<br />\n<strong>解析</strong></p>\n<p>构建初始堆不是一个一个插入，而是从最后一个非叶子结点开始向前调整。</p>\n<p>所以建堆时间复杂度是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>O(n)</span></span></code></pre>\n<p>堆排序时，建堆之后要进行大约 <code>n - 1</code> 次删除堆顶和向下调整。</p>\n<p>每次调整最多走堆的高度：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>O(log₂n)</span></span></code></pre>\n<p>所以堆排序最坏时间复杂度是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>O(nlog₂n)</span></span></code></pre>\n</blockquote>\n<hr />\n<h2 id=\"第-9-题\"><a class=\"anchor\" href=\"#第-9-题\">#</a> 第 9 题</h2>\n<p><strong>题目文字版</strong></p>\n<p>对由相同的 <code>n</code> 个整数构成的二叉排序树和小根堆，下列说法中不正确的是（ ）。</p>\n<p>A. 二叉排序树的高度大于或等于小根堆的高度。<br />\nB. 对二叉排序树进行中序遍历可以得到从小到大的序列。<br />\nC. 从小根堆的根结点到任意叶结点的路径构成从小到大的序列。<br />\nD. 对小根堆进行层次遍历可以得到从小到大的序列。</p>\n<p><strong>答案</strong></p>\n<p>D</p>\n<blockquote>\n<p>[!NOTE]<br />\n<strong>解析</strong></p>\n<p>A 正确。<br />\n小根堆是完全二叉树，结构最紧凑，所以高度较低。普通二叉排序树可能不平衡，高度大于或等于小根堆高度。</p>\n<p>B 正确。<br />\n二叉排序树的中序遍历结果是递增序列。</p>\n<p>C 正确。<br />\n小根堆满足：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>父节点 &#x3C;= 子节点</span></span></code></pre>\n<p>所以从根结点到叶结点的路径上，关键字是非递减的。</p>\n<p>D 错误。<br />\n小根堆只保证父子之间有序，不保证层次遍历整体有序。</p>\n<p>所以选：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>D</span></span></code></pre>\n<hr />\n</blockquote>\n<h2 id=\"第-11-题\"><a class=\"anchor\" href=\"#第-11-题\">#</a> 第 11 题</h2>\n<p><strong>题目文字版</strong></p>\n<p>对关键字序列：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>23, 17, 72, 60, 25, 8, 68, 71, 52</span></span></code></pre>\n<p>进行堆排序，输出两个最小关键字后的剩余堆是（ ）。</p>\n<p>A. <code>&#123;23, 72, 60, 25, 68, 71, 52&#125;</code>​<br />\nB. <code>&#123;23, 25, 52, 60, 71, 72, 68&#125;</code>​<br />\nC. <code>&#123;71, 25, 23, 52, 60, 72, 68&#125;</code>​<br />\nD. <code>&#123;23, 25, 68, 52, 60, 72, 71&#125;</code></p>\n<p><strong>答案</strong></p>\n<p>D</p>\n<blockquote>\n<p>[!NOTE]<br />\n<strong>解析</strong></p>\n<p>要输出两个最小关键字，所以要建立小根堆。</p>\n<p>原序列：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>23, 17, 72, 60, 25, 8, 68, 71, 52</span></span></code></pre>\n<p>建成小根堆后：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>8, 17, 23, 52, 25, 72, 68, 71, 60</span></span></code></pre>\n<p>第一次输出最小值 <code>8</code>。</p>\n<p>用最后一个元素 <code>60</code> 补到堆顶：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>60, 17, 23, 52, 25, 72, 68, 71</span></span></code></pre>\n<p>向下调整后：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>17, 25, 23, 52, 60, 72, 68, 71</span></span></code></pre>\n<p>第二次输出最小值 <code>17</code>。</p>\n<p>用最后一个元素 <code>71</code> 补到堆顶：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>71, 25, 23, 52, 60, 72, 68</span></span></code></pre>\n<p>向下调整：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>23, 25, 71, 52, 60, 72, 68</span></span></code></pre>\n<p>继续调整：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>23, 25, 68, 52, 60, 72, 71</span></span></code></pre>\n<p>所以最终剩余堆是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>23, 25, 68, 52, 60, 72, 71</span></span></code></pre>\n<p>选：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>D</span></span></code></pre>\n<p>注意，B 虽然也是一个合法小根堆，但它不是按照题目给定序列执行堆排序过程得到的结果。</p>\n</blockquote>\n<hr />\n<h2 id=\"第-16-题\"><a class=\"anchor\" href=\"#第-16-题\">#</a> 第 16 题</h2>\n<p><strong>题目文字版</strong></p>\n<p>已知序列：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>25, 13, 10, 12, 9</span></span></code></pre>\n<p>是大根堆，在序列尾部插入新元素 <code>18</code>，再将其调整为大根堆，调整过程中元素之间进行的比较次数是（ ）。</p>\n<p>A. <code>1</code>​<br />\nB. <code>2</code>​<br />\nC. <code>4</code><br />\nD. 图片中被笔迹遮挡，未辨清</p>\n<p><strong>答案</strong></p>\n<p>B</p>\n<blockquote>\n<p>[!NOTE]<br />\n<strong>解析</strong></p>\n<p>原大根堆：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>25, 13, 10, 12, 9</span></span></code></pre>\n<p>插入 <code>18</code> 后：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>25, 13, 10, 12, 9, 18</span></span></code></pre>\n<p>​<code>18</code>​ 的父节点是 <code>10</code>。</p>\n<p>第一次比较：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>18 和 10 比</span></span></code></pre>\n<p>因为：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>18 > 10</span></span></code></pre>\n<p>所以交换：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>25, 13, 18, 12, 9, 10</span></span></code></pre>\n<p>现在 <code>18</code>​ 的父节点是 <code>25</code>。</p>\n<p>第二次比较：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>18 和 25 比</span></span></code></pre>\n<p>因为：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>18 &#x3C; 25</span></span></code></pre>\n<p>满足大根堆，停止。</p>\n<p>所以比较次数是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>2 次</span></span></code></pre>\n<p>这题容易漏掉最后一次“不需要交换”的比较。</p>\n</blockquote>\n<hr />\n<h2 id=\"第-17-题\"><a class=\"anchor\" href=\"#第-17-题\">#</a> 第 17 题</h2>\n<p><strong>题目文字版</strong></p>\n<p>已知小根堆为：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>8, 15, 10, 21, 34, 16, 12</span></span></code></pre>\n<p>删除关键字 <code>8</code> 之后需重建堆，在此过程中，关键字之间的比较次数是（ ）。</p>\n<p>A. <code>1</code>​<br />\nB. <code>2</code>​<br />\nC. <code>3</code>​<br />\nD. <code>4</code></p>\n<p><strong>答案</strong></p>\n<p>C</p>\n<blockquote>\n<p>[!NOTE]<br />\n<strong>解析</strong></p>\n<p>删除堆顶 <code>8</code>​，用最后一个元素 <code>12</code> 补到堆顶：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>12, 15, 10, 21, 34, 16</span></span></code></pre>\n<p>开始向下调整。</p>\n<p>第一次比较左右孩子：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>15 和 10 比</span></span></code></pre>\n<p>较小的是 <code>10</code>。</p>\n<p>第二次比较当前元素和较小孩子：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>12 和 10 比</span></span></code></pre>\n<p>因为：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>12 > 10</span></span></code></pre>\n<p>所以交换：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>10, 15, 12, 21, 34, 16</span></span></code></pre>\n<p>现在 <code>12</code>​ 还有一个孩子 <code>16</code>。</p>\n<p>第三次比较：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>12 和 16 比</span></span></code></pre>\n<p>因为：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>12 &#x3C; 16</span></span></code></pre>\n<p>满足小根堆，停止。</p>\n<p>所以比较次数是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>3 次</span></span></code></pre>\n<p>选：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>C</span></span></code></pre>\n</blockquote>\n<hr />\n<h2 id=\"第-18-题\"><a class=\"anchor\" href=\"#第-18-题\">#</a> 第 18 题</h2>\n<p><strong>题目文字版</strong></p>\n<p>将序列：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>6, 1, 5, 9, 8, 4, 7</span></span></code></pre>\n<p>建成大根堆时，正确的序列变化过程是（ ）。</p>\n<p>A. <code>6,1,7,9,8,4,5 → 6,9,7,1,8,4,5 → 9,6,7,1,8,4,5 → 9,8,7,1,6,4,5</code></p>\n<p>B. <code>6,9,5,1,8,4,7 → 6,9,7,1,8,4,5 → 9,6,7,1,8,4,5 → 9,8,7,1,6,4,5</code></p>\n<p>C. <code>6,9,5,1,8,4,7 → 9,6,5,1,8,4,7 → 9,6,7,1,8,4,5 → 9,8,7,1,6,4,5</code></p>\n<p>D. <code>6,1,7,9,8,4,5 → 7,1,6,9,8,4,5 → 7,9,6,1,8,4,5 → 9,7,6,1,8,4,5 → ...</code></p>\n<p><strong>答案</strong></p>\n<p>A</p>\n<blockquote>\n<p>[!NOTE]<br />\n<strong>解析</strong></p>\n<p>原序列：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>6, 1, 5, 9, 8, 4, 7</span></span></code></pre>\n<p>一共有 7 个元素，最后一个非叶子结点是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>7 / 2 = 3</span></span></code></pre>\n<p>所以从第 3 个位置开始向前调整。</p>\n<p>第一步，调整 <code>5</code>。</p>\n<p>​<code>5</code>​ 的孩子是 <code>4</code>​ 和 <code>7</code>​，较大的是 <code>7</code>。</p>\n<p>交换后：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>6, 1, 7, 9, 8, 4, 5</span></span></code></pre>\n<p>第二步，调整 <code>1</code>。</p>\n<p>​<code>1</code>​ 的孩子是 <code>9</code>​ 和 <code>8</code>​，较大的是 <code>9</code>。</p>\n<p>交换后：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>6, 9, 7, 1, 8, 4, 5</span></span></code></pre>\n<p>第三步，调整 <code>6</code>。</p>\n<p>​<code>6</code>​ 的孩子是 <code>9</code>​ 和 <code>7</code>​，较大的是 <code>9</code>。</p>\n<p>交换后：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>9, 6, 7, 1, 8, 4, 5</span></span></code></pre>\n<p>此时 <code>6</code>​ 下沉到第 2 个位置，它还有孩子 <code>1</code>​ 和 <code>8</code>。</p>\n<p>较大的是 <code>8</code>，继续交换：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>9, 8, 7, 1, 6, 4, 5</span></span></code></pre>\n<p>所以正确变化过程是：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>6,1,7,9,8,4,5</span></span>\n<span class=\"line\"><span>6,9,7,1,8,4,5</span></span>\n<span class=\"line\"><span>9,6,7,1,8,4,5</span></span>\n<span class=\"line\"><span>9,8,7,1,6,4,5</span></span></code></pre>\n<p>选：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>A</span></span></code></pre>\n</blockquote>\n<hr />\n<h1 id=\"三-今天最重要的易错点总结\"><a class=\"anchor\" href=\"#三-今天最重要的易错点总结\">#</a> 三、今天最重要的易错点总结</h1>\n<ol>\n<li>堆插入时，新元素必须先放到末尾，然后向上调整。</li>\n<li>堆删除堆顶时，必须用最后一个元素补到堆顶，然后向下调整。</li>\n<li>建堆从最后一个非叶子结点开始，不是从根节点开始。</li>\n<li>大根堆向下调整时，优先选较大的孩子。</li>\n<li>小根堆向下调整时，优先选较小的孩子。</li>\n<li>比较次数题中，最后一次判断“不交换”的比较也要算。</li>\n<li>合法堆不唯一，但题目问算法过程时，必须按给定过程一步步调整。</li>\n<li>选最大 k 个数用小根堆，选最小 k 个数用大根堆。</li>\n</ol>\n<p>‍</p>\n",
            "tags": [
                "408",
                "数据结构"
            ]
        }
    ]
}