一、C语言编程机制简介

生成程序的具体过程因为计算机环境而异。C 是可移植性语言,因此可以在许多环境中使用,包括 UNIX、Linux、MS-DOS、Windows 和 Macintosh OS。

当我们用C语言编写程序时,编写的内容被存储在纯文本文件中,通常我们称该文件为源代码文件(source code file)。大部分 C 系统,都要求文件名以 .c 结尾(如,Demo.min.c)。
【补充:在文件名中,最后一个(.)号前面的部分称为基本名(basename),其后称之为扩展名(extension)。因此 “Demo.min” 是基本名,“c” 是扩展名。】

图1.1 C 文件名格式

在 C 的编程中,用程序把源代码文件转换为可执行文件(其中包含可直接运行的机器语言代码)是其基本策略。典型的 C 通过编译链接两个步骤来实现这一过程。编译器把源代码转换为中间代码,链接器则把中间代码和其他代码合并,生成可执行文件。
【C 语言这种分而治之的方法便于程序员对程序进行模块化处理。模块化的好处这里不多赘述了,大家自行百度。】

二、什么是中间代码?

前面说了,C 语言的编译器会把源代码文件转换为中间代码文件,那么中间文件到底是什么呢?其实,中间文件文件有很多种形式,通常来说最普遍的一种形式就是 “机器语言代码文件”(我们也可以称之为 “目标文件”)。虽然该文件中存储的是机器语言代码,但是并不能直接运行该文件。因为目标文件中存储的是编译器翻译的源代码,这还不是一个完整的程序。

那怎样才算一个完整的程序呢?那我们得先了解两个个基本概念——启动代码库函数

三、启动代码和库函数

1)什么是启动代码?

启动代码充当着程序和操作系统之间的接口。一般而言,不同的操作系统所需要的启动代码往往是不一样的(例如,Windows 和 Linux),因为这些系统处理程序的方式不同(但也有例外,MS Windows 或 Linux 系统下都能运行 IBM PC 兼容机,这是因为这两个情况使用的硬件相同,所以启动代码相同)。

2)什么是库函数?

库函数(Library function)就是是将函数封装入库,供用户使用的一种方式。方法是把一些常用到的函数编完放到一个文件里,供不同的人进行调用。调用的时候把它所在的文件名用 #include <> 加到里面就可以了(例如,#include <stdio.h>)。

几乎所有的 C 程序都要使用 C 标准库中的函数,例如 printf() 函数和 scanf() 函数。在目标文件中并不包含这些函数的申明和定义,其真正的代码都在 stdio.h 库文件中。当然也有很多标准函数被定义在其他库文件中(如,stdlib.h 中定义了 system() 函数),每个库文件都包含许多函数的目标代码。对于非 C 语言标准库(第三方库)文件,也是类似的。

四、编译器的作用

引自C语言中文网

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

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

编译也可以理解为“翻译”,类似于将中文翻译成英文、将英文翻译成象形文字,它是一个复杂的过程,大致包括词法分析、语法分析、语义分析、性能优化、生成可执行文件五个步骤,期间涉及到复杂的算法和硬件架构。对于学计算机或者软件的大学生,“编译原理”是一门专业课程,有兴趣的读者请自行阅读《编译原理》一书,这里我们不再展开讲解。

注意:不了解编译原理并不影响我们学习C语言,我也不建议初学者去钻研编译原理,贪多嚼不烂,不要把自己绕进去。

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

  • Windows 下常用的是微软编译器(cl.exr),它被集成在 Visual Studio 或 Visual C++ 中,一般不单独使用;
  • Linux 下常用的是 GNU 组织开发的 GCC,很多 Linux 发行版都自带 GCC;
  • Mac 下常用的是 LLVM/Clang,它被集成在 Xcode 中(Xcode 以前集成的是 GCC,后来由于 GCC 的不配合才改为 LLVM/Clang,LLVM/Clang 的性能比 GCC 更加强大)。

你的代码语法正确与否,编译器说了才算,我们学习C语言,从某种意义上说就是学习如何使用编译器,让编译器生成可执行程序(例如 Windows 下的 .exe 程序)。

编译器可以 100% 保证你的代码从语法上讲是正确的,因为哪怕有一点小小的错误,编译也不能通过,编译器会告诉你哪里错了,便于你的更改。

五、链接器的作用

链接器的作用就是把你编写的目标代码、系统标准启动代码和库代码这 3 部分合并成一个文件,即可执行文件。对于库代码,链接器只会把程序中要用到的库函数代码提取出来(见下图)。

图5.1 编译器和链接器

补充:值得注意的是,在有些系统中编译程序和链接程序需要用户分别调用,但是在另一些系统中,编译器会自动启动链接器。

参考文献

  1. C Primer Plus:6th
  2. C语言中文网 - 什么是编译器?