C 语言编译链接机制
一、C语言编程机制简介
生成程序的具体过程因为计算机环境而异。C 是可移植性语言,因此可以在许多环境中使用,包括 UNIX、Linux、MS-DOS、Windows 和 Macintosh OS。
当我们用C语言编写程序时,编写的内容被存储在纯文本文件中,通常我们称该文件为源代码文件(source code file)。大部分 C 系统,都要求文件名以 .c 结尾(如,Demo.min.c)。
【补充:在文件名中,最后一个(.)号前面的部分称为基本名(basename),其后称之为扩展名(extension)。因此 “Demo.min” 是基本名,“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 部分合并成一个文件,即可执行文件。对于库代码,链接器只会把程序中要用到的库函数代码提取出来(见下图)。
补充:值得注意的是,在有些系统中编译程序和链接程序需要用户分别调用,但是在另一些系统中,编译器会自动启动链接器。