https://mp.weixin.qq.com/s/6dJGqQAtxcUfHLOTvhdJOA
实际实践中,为了提高代码复用和灵活性,除了编译过程之外,会增加一些操作过程。
下面以C语言为例,逐个简介。
二. 编译
编译原理的各个过程在这个阶段执行,把C语言代码源文件转换成为目标文件。目标文件即是编译生成的最终代码。
编号为“二”是指编译这个过程在第二步执行。放在最开始,是为了承接上一篇编译原理。
一. 预处理
C语言的预处理,比编译过程要简单,可以简单的理解为文本替换。虽然只是简单的一步文本替换,但是为代码编写提供了很大的灵活性和扩展性。
gcc -E选项,可以把.c转换为.i,检查宏替换是否正确。
三.链接
链接把多个源文件编译生成的目标文件链接到一起,生成可执行文件,如exe和elf格式可执行文件。
因为多个源文件之间存在符号引用问题,所以在编译阶段并不会也无法确定符号的最终地址,在链接生成可执行文件时才确定。
四.库
为了提高代码复用度,很多常用的函数如字符串操作,内存拷贝等,并不会每一程序都会重写一遍,而是事先把这些函数编译成库。在链接应用程序的时候,直接把这些库作为目标文件,一起链接。
如libc, libc++, uClibc等。
库分为静态库和动态库。
静态库在链接时会拷贝到可执行程序中,程序运行时不再需要该库文件;
动态库在链接时只是记录符号引用信息,程序运行时需要能够在库文件查找路径(LD_LIBRARY_PATH)中找到对应的库,才能够成功执行。
五.加载器
应用程序一般由操作系统加载执行,操作系统加载可执行程序时,会依据可执行程序的格式(ELF/EXE),把可执行文件中不同的段加载到相应的地址区间(线性地址区间,非物理地址)。如果可执行程序依赖某个库文件(动态库),则在系统库文件路径中查找所依赖的库,然后把库文件加载进程序地址区间。然后替换程序中所引用的库文件的符号为库中相应函数加载的地址。所有符号替换完成后,程序即可开始执行。
操作系统本身也会提供很多系统库供进程使用,并且使用范围很广。操作系统并不会把这些苦加载到每个进程的地址空间中,而是在物理内存中加载一次,然后把这块内存区间映射到所有进程的地址区间中。这样既可以减少进程加载的时间,也可以防止内存中加载多份相同的库,虚耗内存。
编译-实践中的编译过程
原文:https://www.cnblogs.com/wjcdx/p/9321154.html