在介绍了C编程的基本概念之后,我们现在可以简要地讨论编译过程。
像任何编程语言一样,C本身对于微处理器来说是完全不可理解的。它的目的是提供一种用于人类是提供一种能够很容易地转换成机器代码指令以直观的方式是理解到微处理器。该编译器是什么翻译我们人类可读的源代码转换成机器代码。
对于那些刚接触编程的人来说,这似乎相当简单。一个天真的编译器可能会读入每个源文件,将所有内容转换为机器代码,并写出可执行文件。这可行,但有两个严重的问题。首先,对于大型项目,计算机可能没有足够的内存来一次读取所有源代码。其次,如果对单个源文件进行更改,则必须重新编译整个应用程序。
为了解决这些问题,编译器将工作分解为步骤。对于每个源文件(每个.c文件),编译器读取文件,读取它通过#include指令引用的文件,并将它们转换为机器代码。结果是“目标文件”(.o)。创建所有目标文件后,“链接器”程序将收集所有目标文件并写入实际的可执行程序。这样,如果您更改了一个源文件,则只需要重新编译该文件,之后,需要重新链接该应用程序。
没有详细说明,对编译过程有一个肤浅的理解是有益的。
预处理器
预处理器提供了包含所谓的头文件,宏扩展,条件编译和行控制的能力。很多时候,您需要向编译器提供特殊指令。这可以通过在代码中插入预处理程序指令来完成。当您开始编译代码时,一个称为预处理器的特殊程序会扫描源代码,并根据预定义的规则为其他代码执行令牌字符串的简单替换。C预处理器不是C语言的一部分。
所有预处理程序指令都以井号字符(#)开头。您可以在Hello world程序中看到一个预处理程序指令。例:
#include
该指令使stdio标头包含在您的程序中。其他指令,如#pragma控制编译器设置和宏。预处理阶段的结果是文本字符串。您可以将预处理器视为非交互式文本编辑器,为编译准备代码。预处理程序指令的语言与C的语法无关,因此C预处理程序也可以独立用于处理其他类型的文本文件。
语法检查
此步骤确保代码有效并将序列化为可执行程序。在大多数编译器下,您可能会收到消息或警告,指出程序可能存在的问题(例如条件语句总是为真或假等)
当在程序中检测到错误时,编译器通常会报告阻止编译的文件名和行。
对象代码
编译器生成与源代码等效的机器代码,可以链接到最终程序中。此时代码本身无法执行,因为它需要链接才能执行。
在讨论编译是“单行道”的基础知识之后,请务必注意。也就是说,将C源文件编译成机器代码很容易,但“反编译”(将机器代码转换为创建它的C源代码)则不然。C的反编译器确实存在,但它们创建的代码很难理解,只对逆向工程有用。
链接
联通过集成库和代码并产生任一种结合了单独的对象文件合并为一个完整的程序的可执行程序或库。链接由链接器程序执行,该程序通常是编译器套件的一部分。
此阶段的常见错误是缺少或重复的功能。
自动化
对于大型C项目,许多程序员选择自动编译,以减少用户交互需求并通过重新编译仅修改过的文件来加快进程。
大多数集成开发环境都有某种项目管理,这使得这种自动化非常容易。但是,项目管理文件通常只能由同一集成开发环境的用户使用,因此任何希望修改项目的人都需要使用相同的IDE。
在类UNIX系统上,make和Makefiles通常用于完成相同的操作。Make是传统且灵活的,可作为大多数Unix和GNU发行版的标准开发人员工具之一。
GNU Autotools扩展了Makefile ,由Automake和Autoconf组成,用于使软件可编译,可测试,可翻译和可在许多类型的机器上配置。Automake和Autoconf在各自的手册中有详细描述。
Autotools通常被认为是复杂的,并且已经开发了各种更简单的构建系统。GNOME项目的许多组件现在使用声明性的Meson构建系统,该系统不太灵活,而是专注于以简单的方式提供构建系统中最常需要的功能。用C语言编写的程序的其他流行的构建系统包括CMake和Waf。
安装gcc后,可以使用已编写但尚未编译的c源文件列表调用它。例如,如果文件main.c包含myfun.h中描述的函数并在myfun_a.c和myfun_b.c中实现,那么就足够了
gcc main.c myfun_a.c myfun_b.c
myfun.h包含在main.c中,但如果它位于单独的头文件目录中,那么该目录可以在“-I”开关后列出。
在较大的程序中,Makefile和gnu make程序可以将c文件编译成以后缀.o结尾的中间文件,可以通过gcc链接。
如何编译每个目标文件通常在Makefile中描述,目标文件作为标签以冒号结尾,后跟两个空格(制表符经常导致问题),后面是依赖项的其他文件列表,例如.c文件和.c文件。 o在另一个部分编译的文件,在下一行,调用所需的gcc。打字 man make 或 info make经常提供如何使用make以及gcc所需的信息。虽然gcc有很多选项开关,但经常使用的是-g来生成gdb的调试信息,以允许gdb在机器代码程序的逐步执行期间显示源代码。gdb有一个’h’命令显示它可以做什么,如果a.out是由gcc编译的匿名可执行机器代码文件,它通常以’gdb a.out’开头。