⚙️ 第八章 从源文件到可执行文件
📄 一句话概括

CPU 只认本机代码——源码和中间码它都读不懂;换一种 CPU,同一份代码就得重编。

📝 本节和前面几节的关系

第七章讲了程序跑在什么之上,这一章回到起点:你写下的那个源文件,究竟怎么变成能跑的东西。

一、CPU 只认一种语言

CPU 只会读一种东西:本机代码。 你写的源码它读不懂;编译出来的中间码它也读不懂——只有这一种 CPU 的指令它认。 所以得有一连串翻译:源码 → 目标代码 → 可执行文件。 这一章走的就是这条翻译链,一步都不会跳过。

二、文件头上的身份标记

打开一个真实的系统 DLL 看看开头:头两个字节是 MZ。 再往后能找到 PE 这个标记——它们合起来说明【这是给 Windows 用的可执行格式】。 所以可执行文件并不神秘:它是一串有固定头部的字节,头部告诉系统【怎么把它装进内存】。 而头部之后的那一大部分,才是真正的指令和数据。

三、本机代码是给 CPU 的,不是给人的

同样的逻辑,写成源码、写成中间码、写成本机代码,表达的意思是一样的。 差别只在谁读得懂:人、虚拟机、还是 CPU 本身。 所以【编译】这件事本质上是换一种读者——把给人和给虚拟机的说明,改成给 CPU 的说明。 也正因为读者是具体的某一类 CPU,本机代码天生不具备跨平台性。

四、所以跨平台从来不是自动的

换一种 CPU,指令集就变了,本机代码直接失效。 这就是 7.2 说的那句【Windows 吸收不了 CPU 的差异】在文件层面的样子。 想要跨平台,要么为每个平台各编一份,要么在中间垫一层虚拟机(7.5)。 这一章讲的是前一条路:老老实实编成本机代码,只是把过程拆开看清楚。

五、动手 Demo:计算机只能执行本机代码

CPU 只会读一种东西:本机代码。 打开一个真实的系统 DLL,看它的身份标记。

import os, platform
p = r"C:\Windows\System32\kernel32.dll"
with open(p, "rb") as f:
    head = f.read(256)
print("大小      ", os.path.getsize(p))
print("头两个字节", head[:2])
print("处理器    ", platform.processor())

实跑输出:

--------------------------------------------------------------------
计算机只能执行本机代码
--------------------------------------------------------------------

**CPU 只会读一种东西:本机代码。**
你写的源码它读不懂,编译出来的中间码它也读不懂——**只有这一种 CPU 的指令它认。**

  层次             是什么                 谁读得懂
  ---------------------------------------------------------
  你写的源码       文本文件               只有人
  编译产物         本机代码(二进制)     只有这一种 CPU
  系统里的 DLL     同样是本机代码         同样是这一种 CPU

  打开一个真实的系统 DLL,看它的身份标记:

    文件        C:\Windows\System32\kernel32.dll
    大小        773792 字节
    头两个字节  b'MZ'(十六进制 4d 5a)
    里面的 PE 标记  在第 248 字节处
    **MZ 与 PE 是 Windows 可执行文件的身份标记。**
    这台机器的 CPU   Intel64 Family 6 Model 94 Stepping 3, GenuineIntel

  **这份 DLL 里的每一个字节,都是这台机器(这一类 CPU)才读得懂的指令。**

实测的 kernel32.dll:773792 字节,头两个字节是 b'MZ',PE 标记在第 248 字节处。 这一串字节,只有这一类 CPU 才读得懂。

30 秒实验

  1. 找一个 exe 文件,用记事本打开看看开头几个字符是什么。
  2. 想一想:为什么同一个软件要按 32 位/64 位分别下载。

常见误区

  1. 以为源码能直接跑。CPU 只认本机代码。
  2. 以为 exe 只是一堆代码。它还有固定的头部,告诉系统怎么装载它。
自测三问(点开对答案) 1. CPU 能直接执行什么? —— 本机代码——这一种 CPU 自己的指令;源码与中间码它都读不懂。 2. Windows 可执行文件的身份标记是什么? —— 开头的 MZ,以及文件内部的 PE 标记。 3. 为什么本机代码不能跨平台? —— 它是为某一种 CPU 的指令集编出来的,换成别的指令集就失效。

相关笔记