跳转到内容

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 扩展,由 AutomakeAutoconf 组成,用于使软件在多种类型的机器上可编译、可测试、可翻译和可配置。Automake 和 Autoconf 在其各自的手册中详细描述。

Autotools 通常被认为很复杂,因此开发了各种更简单的构建系统。现在,GNOME 项目 的许多组件现在使用声明式的 Meson 构建系统,它不太灵活,但专注于以简单的方式提供构建系统中最常用的功能。用于用 C 语言编写的程序的其他流行的构建系统包括 CMakeWaf

安装 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 makeinfo make 通常会提供有关如何使用 make 和 GCC 的信息。

尽管 GCC 具有很多选项开关,但经常使用的一个选项是 -g,它用于为 gdb 生成调试信息,以使 gdb 能够在单步执行机器代码程序时显示源代码。 gdb 具有一个“h”命令,用于显示其功能,通常用“gdb a.out”启动,如果 a.out 是由 GCC 编译的匿名可执行机器代码文件。


华夏公益教科书