编译与链接的整个过程:
预处理Prepressing->编译Compilation->
汇编Assembly->链接Linking
以helloworld作为例子展开:
-
预处理
g++ -E hello.cpp -o hello.i -> hello.i
(1) 将#define的内容替换
(2) 处理条件预编译指令,如#if ,#ifdef等
(3) 处理#include预编译指令
(4) 过滤注释
(5) 添加行号及文件标示便于编译报错
(6) 保留#pragma编译器指令 -
编译+汇编
g++ -S hello.cpp -o hello.s -> hello.s 高级语言编译成汇编指令文件
g++ -c hello.cpp -> hello.o 汇编指令汇编为可供机器使用的机器语言(二进制)
(1) 词法分析,扫描源代码文件,将字符序列分成一个个记号
(2) 语法分析,将记号进行语法分析得到语法数,每一个结点是一个表达式。
(3) 语义分析,静态语义分析,分析语句是否真正有含义,包括类型的声明以及类型的匹配和转换。结束后,整个语法树的表达式都被标识了类型。
(4) 中间优化语言,对源代码级的表达式进行优化,如2+7直接为9。
(5) 目标代码的生成及优化,鉴于不同的目标机器,将中间语言转化成合适的目标代码(不同的机器的类型字长等不相同),最后将其优化(乘法用偏移代替,合适的寻址方式等)。
编译的整个过程:
3. 链接
将每个源代码模块独立编译,然后按照要求组装起来的过程为链接。
原理:把一些指令对其它符号地址的引用加以修正,包括了地址和空间分配,符号决议,重定位这些步骤。
过程:将.o的目标文件和库一起链接形成最终可执行文件。库就是一组目标文件的包(最常用的代码编译成目标文件打包存放).
采用自定义的add与sub来说明。
首先编写add.h,add.cpp,sub.h,sub.cpp,main.cpp
(1) 静态链接
对函数库的链接放在编译时期完成的是静态链接,库采用libxxx.a。
g++ -o add.cpp
g++ -o sub.cpp
ar cr libmymath.a add.o sub.o //将两个目标文件放入静态库
// ar命令的c选项为创建一个库,r选项为库中插入模块
(ar tv libmymath.a可以查看静态库)
g++ -o main main.cpp libmymath.a
./main**
(2) 动态链接
把对一些库函数的链接载入退出到程序运行时期,采用libxxx.so。
g++ -fPIC -shared -o libmymath.so add,.o sub.o**
// -fPIC 编译为位置独立的代码,默认位置相关,动态载入时通过代码复制的方式满足不同进程的需要
g++ -o main main.cpp libmymath.so
./main**
链接没有问题,在执行时报错,显示No such file or directory。原因是程序运行时会在/usr/lib等目录查找需要的动态库文件。
解决方法:将libmymath.so复制到/usr/lib,修改环境变量**LD_LIBRARY_PATH。
cp libmymath.so /usr/lib
export LD_LIBRARY_PATH=/usr/lib:$LD_LIBRARY_PATH
sudo ldconfig**
(3) 静态库与动态库
- 在两个重名的静态库和动态库中,优先调用动态库。
- 在程序需要调用动态库中的函数时,会先在内存中查找有无库函数拷贝,没有才会复制。但是静态库会在每个程序中复制其库函数拷贝,造成大量的开销。
- 库函数发生变化时,静态库需要重新编译,动态库只要提供的接口没变,无需重新编译。
- 动态库的函数的调用可由程序员控制。
- 由于静态库的函数编译时已经加载了,因此程序执行的速度会快些。