引言
通过高级语言编写的源码,我们能够轻松理解其逻辑与意图,但对于计算机硬件来说,它只认识二进制指令。源码对人类而言是友好的文本,对计算机来说则是无法识别的天书。因此,源码要想被执行,必须先转换成计算机 CPU 能够直接识别并执行的二进制机器码。
所谓二进制指令,也就是由 0 和 1 组成的机器码,这是计算机底层唯一能理解的指令集。然而,究竟在什么时候将源代码转换成二进制指令呢?不同的编程语言有不同的规定和处理机制,这直接决定了语言的分类和执行效率。
编译型与解释型的定义
根据转换时机和方式的不同,编程语言主要分为两大类:
- 编译型语言:要求必须提前将所有源代码一次性转换成二进制指令,生成一个可执行程序(例如 Windows 下的 .exe 文件)。C 语言、C++、Golang、汇编语言等属于此类,使用的转换工具称为编译器。一旦编译完成,生成的程序即可独立运行。
- 解释型语言:可以一边执行一边转换,需要哪些源代码就转换哪些源代码,通常不会生成独立的二进制可执行文件。Python、JavaScript、PHP、Shell 等属于此类,使用的转换工具称为解释器。
简单地理解,编译器和解释器其实就是一个'翻译工具',负责将人类可读的源代码翻译成机器可读的指令。对源代码进行'翻译'是一个很复杂的过程,大致包括词法分析、语法分析、语义分析、性能优化、生成可执行文件等五个步骤,期间涉及到复杂的算法和硬件架构。
执行流程详解
编译型语言流程
对于编译型语言,在执行之前先要经过编译器将源码转换成 CPU 可识别的机器码文件。这个过程中,源代码被完全读取,检查错误,然后生成目标代码。只要拥有这个可执行程序,就可以随时运行,不需要再重新编译,也不需要源代码和编译器参与运行过程。这就是所谓的'一次编译,无限次运行'。
解释型语言流程
对于解释型语言,每次执行程序都需要一边转换一边执行。用到哪些源代码就将哪些源代码转换成机器码,用不到的则不进行任何处理。由于每次执行程序都需要重新转换源代码,所以解释型语言的执行效率天生就低于编译型语言,甚至是数量级的差距。因此,计算机的一些底层功能,或者关键算法,一般都使用 C/C++ 实现,只有在应用层面(比如网站开发、批处理、小工具等)才会使用解释型语言。
在运行解释型语言的时候,我们始终都需要源代码和解释器,所以说它无法脱离开发环境。例如,当我们说'下载一个程序(软件)'时,不同类型的语言有不同的含义:
- 对于编译型语言,我们下载到的是可执行文件,源代码被作者保留,所以编译型语言的程序一般是闭源的;
- 对于解释型语言,我们下载到的是所有的源代码,因为作者不给源代码就没法运行,所以解释型语言的程序一般是开源的。
混合模式语言
除编译型和解释型语言外,还有一种半编译半解释型语言,比如 Java 和 C# 等。这类语言将源代码先转换成一种中间文件(字节码文件),然后再将中间文件拿到虚拟机中执行。这种模式结合了编译型和解释型的优点,既保证了跨平台性,又通过即时编译(JIT)技术提升了运行效率。
编译型语言深度分析
特点与优势
- 执行效率高:由于代码在运行前已经全部转换为机器码,CPU 可以直接执行,无需额外的翻译开销,因此在性能敏感的场景下表现优异。
- 安全性较高:发布的是二进制文件,源代码不公开,逆向工程难度较大,有利于保护知识产权。
- 部署简单:用户只需安装生成的可执行文件,无需安装额外的开发环境或运行时库(除非依赖系统库)。
局限与挑战
- 跨平台性差:编译型语言通常是不能跨平台的,也就是不能在不同的操作系统之间随意切换。不同操作系统对可执行文件的内部结构有着截然不同的要求,彼此之间也不能兼容。比如,你不能将 Windows 下的可执行程序拿到 Linux 下使用,也不能将 Linux 下的可执行程序拿到 macOS 下使用。
- 相同操作系统的不同版本之间也不一定兼容:比如不能将 x64 程序(Windows 64 位程序)拿到 x86 平台上(Windows 32 位平台)运行。但反之一般可行,因为 64 位 Windows 对 32 位程序做了很好的兼容性处理。
- 源代码移植困难:不同平台支持的函数、类型、变量等都可能不同,基于某个平台编写的源代码一般不能拿到另一个平台直接运行。下面以 C 语言为例进行说明。
- 在 C 语言中,要想让程序暂停,我们可以使用'睡眠'函数。在 Windows 平台下该函数是 Sleep(),并以毫秒为时间单位,而在 Linux 平台下则是 sleep(),以秒为单位。可以看出,首先两个函数的首字母大小写不同,再者 Sleep() 的参数是毫秒,而 sleep() 的参数是秒,单位也不一样。


