编译的过程
当我们用gcc编译c语言程序的时候都是直接生成可执行文件,但是在实际的编译过程中
编译的过程也是有明显的步骤之分的。
以最简单的程序hello.c为例。
1:预编译gcc -E hello.c -o hello.i
简单来说就是替换掉宏,展开头文件。1
2
3
4
5
6将所有的`
处理所有条件预编译指令,比如`
处理`include`预编译指令,将被包含的头文件插入到该预编译指令的位置。(这个过程是递归进行的,也就是被包含的文件可能包含有其他的文件)
删除所有的注释`//`和`/**/`
添加行号和文件名标识,比如`
保留所有的`
2:编译gcc -S hello.i -o hello.s
简单来说是将c语言程序编译成汇编代码。1
2编译过程就是把预处理完的文件进行一系列的
词法分析、语法分析、语义分析以及优化后产生向以的汇编代码文件。
3:汇编
简单来说汇编是将汇编代码转换成可重定向的二进制文件。gcc -c hello.s -o hello.o
1
2
3
4
5
6汇编是用汇编器将汇编代码转变成机器可以只行的指令,
每一个汇编预言语句几乎都对应一条机器指令。
所以汇编器的汇编过程相对于编译器来讲比较简单,
它没有复杂的语法,也没有语义,也不需要做指令优化,
值是根据汇编指令和机器指令的对照表一一翻译就可以了,
`汇编这个名字也来源于此`。
4:链接
简单来说就是将可重定向文件与库文件链接生成可执行的二进制文件。1
2
3链接也有两种方式:
一种是与静态库的链接
一种是与动态库的链接
本文主要是来区分两种链接方式的不同用法
链接
首先看以下所需要的几个文件hello.h
1
2
3
4
5
6
void pirnt();
hello.c
1
2
3
4
5
6
7
void print()
{
printf("hello,world\n");
}
main.c
1
2
3
4
5
6
7
8
9
int main(int argc, char **argv)
{
print();
return 0;
}
无论时静态库还是静态库,都是由.o文件打包形成的
前面的预编译和编译就省略,直接到汇编生成可重定向的二进制文件1
2
3
4[root@linux demo]
[root@linux demo]
[root@linux demo]
hello.c hello.h hello.o main.c main.o
链接操作
这里是直接用的是gcc的链接1
2
3
4
5[root@linux demo]
[root@linux demo]
hello.c hello.h hello.o main main.c main.o
[root@linux demo]
hello,world
静态库链接的运作方式
静态库一般是以.a结尾,lib开头。
实际上呢,静态库就是对汇编生成的可重定向的二进制文件的XXX.o
文件的打包
打包是为了提高代码的利用率?
那么如何将.o文件打包成静态库同时在链接的时候使用静态库呢?
比如hello.o
文件,当其他程序要使用hello.o
中的函数时,把hello.h
文件包含进去
1 |
|
动态链接的运作方式
动态库一般是以.so结尾1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@linux demo]
[root@linux demo]
[root@linux demo]
./main: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory
[root@linux demo]
ldconfig: relative path libhello.so used to build cache
[root@linux demo]
[root@linux demo]
hello,world
使用静态库和动态库的优缺点
其实优缺点从他们使用的方式就体现了出来
静态库时在链接的时候,把库的代码都链接了过来,这样静态链接的程序可以不依赖于
库从而可以独立运行,而动态链接的程序,当它所需要的库还没有加载到内存的时候是
不能够运行的。
静态库每次都要把代码复制到程序中,这样程序本身就需要占用磁盘空间,在程序
运行的时候也是非常消耗内存的,如果有很多程序都使用了相同的库,那样就更是浪费
了。
而动态库的链接只是引用了动态库的符号表,并不会把其他的并入到程序中。
同时程序在运行时,使用了相同动态库的程序会共用那个动态库。
但是,在系统开始运行使用了动态链接的程序时,系统会先把控制权交给动态链接器,
由它完成所有的动态链接过程工作之后再把控制权交给程序。这样缺点也显而易见了,
执行过程慢,程序在运行前还要先调用动态链接器。
总结:
静态库:
优点:执行速度快,编译好之后不需要依赖其它库就可以运行
缺点:浪费空间,程序存放时会占用硬盘空间,执行的时候会占用内存
动态库:
优点:节省空间,可以实现多个程序在运行时使用同一个库,节省磁盘空间和内存
缺点:执行速度较慢,在程序的时候要先执行动态链接操作。