C 编程/编译基础
在介绍了 C 编程的基本概念后,我们现在可以简要讨论一下编译的过程。
像任何编程语言一样,C 本身对 微处理器 来说是完全不可理解的。它的目的是为人类提供一种直观的方式来提供指令,这些指令可以很容易地转换为微处理器可以理解的机器代码。编译器就是将我们人类可读的源代码翻译成机器代码的工具。
对于编程新手来说,这似乎很简单。一个简单的编译器可能会读取每个源文件,将所有内容翻译成机器代码,然后输出可执行文件。这可能行得通,但有两个严重的问题。首先,对于一个大型项目,计算机可能没有足够的内存来一次读取所有源代码。其次,如果你对一个源文件进行修改,你将不得不重新编译整个应用程序。
为了解决这些问题,编译器将工作分解成几个步骤。对于每个源文件(每个.c
文件),编译器读取文件,读取它通过#include
指令引用的文件,并将它们翻译成机器代码。这将产生一个“目标文件”(.o
)。在所有目标文件创建之后,一个“链接器”程序会收集所有目标文件并写入实际的可执行程序。这样,如果你更改一个源文件,就只需要重新编译该文件,然后重新链接应用程序。
在不深入细节的情况下,了解编译过程的表面理解是有益的。
预处理器提供了包含所谓的头文件、宏扩展、条件编译和行控制的能力。这些功能可以通过在代码中插入相应的预处理器 指令 来访问。在编译源代码之前,一个特殊的程序,称为预处理器,会扫描源代码中的标记,或特殊字符串,并根据特定的规则将它们替换成其他字符串或代码。C 预处理器从技术上来说不是 C 语言的一部分,而是你的编译器软件提供的工具。
所有预处理器指令都以井号 (#) 开头。你可以在 Hello world 程序 中看到一个预处理器指令。示例
#include <stdio.h>
此指令会导致 stdio 头文件包含到你的程序中。其他指令,如#pragma
,控制编译器设置和宏。预处理阶段的结果是一个文本字符串。你可以将预处理器视为一个非交互式文本编辑器,它会修改你的代码以使其准备好进行编译。预处理器指令的语言与 C 的语法无关,因此 C 预处理器也可以独立地用于处理其他类型的文本文件。
此步骤确保代码有效,并将顺序排列成一个可执行程序。在大多数编译器下,你可能会收到消息或警告,指示你的程序中可能存在问题(例如,条件语句 始终为真或为假等等)。
当在程序中检测到错误时,编译器通常会报告阻止编译的文件名和行号。
编译器会生成源代码的机器代码等价物,可以将其链接到最终程序中。此时,代码本身不能执行,因为它需要链接才能执行。
在讨论完基础知识之后,需要注意的是,编译是“单行道”。也就是说,将 C 源文件编译成机器代码很容易,但“反编译”(将机器代码转换成创建它的 C 源代码)则不然。C 的反编译器确实存在,但它们创建的代码难以理解,只有在 逆向工程 中才有用。
链接通过集成库和代码将不同的目标文件组合成一个完整的程序,并生成 可执行程序 或 库。链接由链接器程序执行,该程序通常是编译器套件的一部分。
此阶段常见的错误是缺少或重复的函数。
对于大型 C 项目,许多程序员选择自动化编译,既是为了减少用户交互需求,也是为了通过仅重新编译修改过的文件来加快过程。
大多数集成开发环境 (IDE) 都有某种项目管理功能,这使得这种自动化变得非常容易。但是,项目管理文件通常只能由使用相同集成开发环境的用户使用,因此任何希望修改项目的人都需要使用相同的 IDE。
在类 UNIX 系统上,make 和 Makefile 通常用于实现相同的目的。make 是一种传统且灵活的方法,在大多数 Unix 和 GNU 发行版上都作为标准开发工具之一提供。
Makefile 已通过 GNU Autotools 扩展,由 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 文件和在另一节中编译的 .o 文件,并在下一行,调用所需的 GCC。
键入man make
或info make
通常会提供有关如何使用 make 以及 GCC 的信息。
尽管 GCC 有许多选项开关,但常用的一个选项是 -g,它用于生成供 gdb 调试使用的调试信息,以便 gdb 在机器代码程序的逐行执行过程中显示源代码。gdb 具有一个“h”命令,它显示了 gdb 的功能,通常通过“gdb a.out”启动,其中 a.out 是由 GCC 编译生成的匿名可执行机器代码文件。