源文件是如何转换成可执行文件的
几个小知识点
-
CPU可以解析和运行的程序形式为本地代码
-
链接器将多个目标文件结合生成.exe文件
-
.obj文件为本地代码
-
收录多个目标文件的是库文件
链接器从库文件中抽取出必要的目标文件,并将其结合到exe文件中
此外,还存在一种程序运行时结合的dll形式的库文件
-
仅包含Windows的.dll文件中存储的函数信息的文件为导入库
把导入库信息结合到exe文件中,这样程序在运行时就可以利用dll内的函数了
-
程序运行时,动态申请分配的数据和对象的内存区域形式为堆,堆的内存空间会根据程序的指令进行申请和释放
本地代码的内容
dump是指把文件的内容,每个字节用2位十六进制来表示的方式
本地代码的内容就是各种数值的罗列,可以把exe
文件的内容Dump一下,其实得到的本地代码的内容就是各种数值的罗列。每个数值都表示一个命令或数据,这就是原始的Dump程序。
编译器负责转换源代码
编译器能够把C语言等高级编程语言编写的源代码转换成本地代码(.c—>.obj)
每个编写源代码的编程语言都需要其专门的编译器
编译器读入源代码时,要经过语法解析、句法解析、语义解析等,才能成成本地代码。
根据CPU类型的不同,本地代码的类型也不同。编译器不仅和编程语言的种类有关,还和CPU的类型是相关的。同时编译器作为一种程序,也需要运行环境。(如Windows的C编译器)
因此选择编译器,需要兼顾的问题有:
- 何种编程语言的编译器
- 编译器生成的本地代码适用于那种CPU
- 该编译器在什么环境下使用
链接器链接得到EXE文件
编译后得到的文件为.obj
目标文件,虽然目标文件的内容是本地代码,但是无法直接运行,原因就是:当前程序还处于未完成状态。故.obj
文件是尚未完成的本地代码。
#sample.c
#include <windows.h>
#include <stdio.h>
//消息框的标题
char* title ="test program";
//返回两个参数的平均数
double Average(double a,double b){
return (a+b)/2;
}
// 程序运行起始位置的函数
int WINAPI WinMain(HINSTANCE h,HINSTANCE d, LPSTR s,int m){
double ave; //保存平均数的变量
char buff[80]; //保存字符串的变量
ave=Average(123,456);
//编写显示在消息框中的字符串
sprintf(buff,"aver=%f",ave);
//打开消息框
MessageBox(NULL,buff,title,MB_OK);
}
上面的源代码生成的目标文件为sample.obj
Average()
用来返回两个参数的平均值;WinMain()
是程序的运行起始函数,函数Average()
和函数WinMain()
是我们自己编写的,处理的内容记述在源代码中。
sprintf()
是通过指定格式把数值变换成字符串的函数;MessageBox()
是消息框函数。不过代码中并没有记述这两个函数的处理内容。因此就必须将存储这sprintf()
和MessageBox()
的处理内容的目标文件(假设为c0w32.obj
)和sample.obj
结合,否则处理就不完整,.exe
文件就无法完成
- 把多个目标文件(obj文件)结合,最终生成一个exe文件的处理就是链接,运行链接的程序就是链接器(linkage editor)
库文件
- 库文件指的是把多个目标文件集合保存到一个文件中的形式。
链接器指定库文件后,就会从中把需要的目标文件抽取出来,并同其他目标文件结合生成exe
文件
Error:无法解析的外部符号'_sprintf'.......
表示的是无法找到记述着目的变量及函数的目标文件,因而无法进行链接的意思。因此需要导入文件包。
sprintf()等函数,不是通过源代码形式而是通过库文件形式和编译器一起提供的,这样的函数成为标准函数。
通过库文件,可以减少链接器的压力。
DLL文件及导入库
Windows以函数的形式为应用提供了各种功能,这些形式的函数成为API(Application Programming Interface),例如MessageBox()不是标准函数,而是Windows提供的API的一种。
在Windows中,API的目标文件,并不是存储在通常的库文件中,而是存储在dll(Dynamic Link Library)文件的特殊库文件中。
- dll文件是程序运行是动态结合文件。
前面提到的MessageBox()
的目标文件是存储在import32.lib
中的。
MessageBox()
的目标文件的实体实际并不存在。
实际上,该import32.lib
文件存储着两个信息:
-
MessageBox()
在user32.dll
这个dll
文件中 -
存储着
dll
文件的文件夹信息
- 把
import.lib
这样的文件称为导入库。
相反,存储这目标文件的实体,并直接和exe
文件结合的库文件成为静态链接库(static)
如:存储sprintf()的目标文件cw32.lib就是静态链接库。
通过结合导入库文件,执行时从dll文件中调出对应的函数就会和exe文件进行结合
Windows中的编译和链接机制
补充点:
编译器是在运行前对所有源代码进行解释处理的。
解释器是运行时对源代码的内容一行一行地进行解释处理的。
参考书籍:《程序是怎样跑起来的》