2、编译和链接

编译和链接

平时所说的程序,是指双击后就可以直接运行的程序,这样的程序被称为可执行程序(Executable Program)。在 Windows 下,可执行程序的后缀有.exe.com(其中.exe比较常见);在类 UNIX 系统(Linux、Mac OS 等)下,可执行程序没有特定的后缀,系统根据文件的头部信息来判断是否是可执行程序。

可执行程序的内部是一系列计算机指令和数据的集合,它们都是二进制形式的,CPU 可以直接识别,毫无障碍;但是对于程序员,它们非常晦涩,难以记忆和使用,例如

puts("VIP会员");

对应的可执行文件二进制写法是

img

编译(Compile)

C语言代码由固定的词汇按照固定的格式组织起来,简单直观,程序员容易识别和理解,但是对于CPU,C语言代码就是天书,根本不认识,CPU只认识几百个二进制形式的指令。这就需要一个工具,将C语言代码转换成CPU能够识别的二进制指令,这个工具是一个特殊的软件,叫做编译器(Compiler)

注意:通常编译后的代码不能直接执行,编译只是把C语言代码转换成机器码,但是还缺少启动代码(startup code)和库代码。

编译器能够识别代码中的词汇、句子以及各种特定的格式,并将他们转换成计算机能够识别的二进制形式,这个过程称为编译(Compile)

C语言的编译器有很多种,不同的平台下有不同的编译器,例如:

链接(Link)

C语言代码经过编译以后,并没有生成最终的可执行文件(.exe 文件),而是生成了一种叫做目标文件(Object File)的中间文件(或者说临时文件)。目标文件也是二进制形式的,它和可执行文件的格式是一样的。对于 Visual C++目标文件的后缀是.obj;对于 GCC目标文件的后缀是.o

为什么不会直接生成可执行文件?因为编译只是将我们自己写的代码变成了二进制形式,它还需要和系统组件(比如标准库、动态链接库等)结合起来,这些组件都是程序运行所必须的。

链接(Link)其实就是一个打包的过程,它将所有二进制形式的目标文件和系统组件组合成一个可执行文件。完成链接的过程也需要一个特殊的软件,叫做链接器(Linker)

在有些系统中,必须分别运行编译程序和链接程序,而在另一些系统中,编译器会自动启动链接器,用户只需给出编译命令即可。

多个源文件中,编译器每次只能编译一个源文件,生成一个目标文件,这个时候,链接器除了将目标文件和系统组件组合起来,还需要将编译器生成的多个目标文件组合起来。

编译是针对一个源文件的,有多少个源文件就需要编译多少次,就会生成多少个目标文件。

image-20231101151007183

编译器

以前,UNIX C编译器要调用语言定义的cc命令。但是,它没有跟上标准发展的脚步,已经退出了历史舞台。但是,UNIX 系统提供的C编译器通常来自一些其他源,然后以cc命令作为编译器的别名。

C语言编译器的分类

桌面操作系统

对于当前主流桌面操作系统而言,可使用 Visual C++(MSVC、VC)、GCC以及 LLVM Clang 这三大编译器。

MSVC

Visual C++(简称 MSVC)是由微软开发的,只能用于 Windows 操作系统,它不开源。用户可以使用 Visual Studio Community 版本来免费使用它,但是如果要把通过 Visual Studio Community 工具生成出来的应用进行商用,那么就得好好阅读一下微软的许可证和说明书了。而使用 GCC 与 Clang 编译器构建出来的应用一般没有任何限制,程序员可以将应用程序随意发布和进行商用。

GCC

GNU 项目始于1987 年,是一个开发大量自由UNIX 软件的集合(GNU 的意思是GNU ’s Not UNIX,即GNU 不是UNIX )。GNU 编译器集合(也被称为GCC ,其中包含GCC C编译器)是该项目的产品之一。GCC 在一个指导委员会的带领下,持续不断地开发,它的C编译器紧跟C标准的改动。GCC 有各种版本以适应不同的硬件平台和操作系统,包括UNIX 、Linux 和Windows 。用gcc命令便可调用GCC C编译器。许多使用gcc 的系统都用cc作为gcc的别名。

LLVM Clang

LLVM 项目成为cc的另一个替代品。该项目是与编译器相关的开源软件集合,始于伊利诺伊大学2000 年的研究项目。它的Clang编译器处理C代码,可以通过clang调用。有多种版本供不同的平台使用,包括Linux。2012 年,Clang 成为FreeBSD 的默认C编译器。Clang 也对最新的C标准支持得很好。

嵌入式系统

而在嵌入式系统方面,可用的C语言编译器就非常丰富了,比如:

通常,用于嵌入式系统开发的编译工具链都没有免费版本,而且一般需要通过国内代理进行购买。所以,这对于个人开发者或者嵌入式系统爱好者而言是一道不低的门槛。

不过 Arduino 的开发套件是可免费下载使用的,并且用它做开发板连接调试也十分简单。Arduino 所采用的C编译器是基于 GCC 的。

还有像树莓派(Raspberry Pi)这种迷你电脑可以直接使用 GCC 和 Clang 编译器。此外,还有像 nVidia 公司推出的 Jetson TK 系列开发板也可直接使用 GCC 和 Clang 编译器。树莓派与 Jetson TK 都默认安装了 Linux 操作系统。

在嵌入式领域,一般比较低端的单片机,比如 8 位的 MCU 所对应的C编译器可能只支持 C90 标准,有些甚至连 C90 标准的很多特性都不支持。因为它们一方面内存小,ROM 的容量也小;另一方面,本身处理器机能就十分有限,有些甚至无法支持函数指针,因为处理器本身不包含通过寄存器做间接过程调用的指令。

而像 32 位处理器或 DSP,一般都至少能支持 C99 标准,它们本身的性能也十分强大。而像 ARM 出的 RVDS 编译器甚至可用 GNU 语法扩展。