在之前的C语言的博客中,我们有谈到过编译与链接
从源代码到可执行程序要经过如下过程:
- 预处理(进行宏替换)
- 编译(将C语言代码翻译成汇编代码)
- 汇编(将汇编代码解析成二进制指令)
- 链接(生成可执行程序)
本篇博客就是对上述操作的介绍与分析……
一、gcc/g++如何完成
指令: gcc/g++ 源文件
二、执行gcc/g++每一步操作
2.1 预处理
指令:gcc/g++ -E 源文件 -o 执行完文件名称.i
这一步主要进行宏替换,文件包含,去注释,条件编译等,-E表示开始执行翻译,执行完预处理就停止, .i 表示文件已为预处理过的C语言文件
2.2 编译
指令:gcc/g++ -S 文件名 -o 执行完文件名称.s
程序在这一步进行编译,将C语言文件翻译成汇编文件,-S表示执行到编译结束就停下来,.s 表示该文件为编译过的文件
2.3 汇编
指令:gcc/g++ -c 文件名 -o 执行完文件名称.o
程序在这一步进行汇编,将汇编文件翻译成二进制文件,-c表示执行到汇编结束就停下来,.o 表示该文件为汇编过的文件
2.4 链接
指令:gcc/g++ 文件名 -o 执行完的文件名(默认为a.out)
程序在这一步进行链接,将多文件文件链接到一起,形成可执行程序 ./可执行程序名称即可运行
三、动、静态库的认识
3.1 函数库
我们的头文件一般存放在user/include目录下
我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?
系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用
我们可以通过ldd指令来查看可执行程序使用了哪些库
这里我们通过查看发现ll和我们所写的test程序都包含libc.so.6这个动态库,因此实际上我们的指令也是一个个可执行程序
讨论:是先有的语言还是先有的编译器?
先有的编译器然后才有语言,只不过第一版的编译器是由上一代语言写的而已,而后语言/编译器实现了自举
3.2 链接的过程
程序在链接时有两种方式:
- 动态链接
- 静态链接
3.2.1 动态库和静态库
动态库在linux中后缀是.so,在windows中后缀是.dll
静态库在linux中的后缀是.a,在windows中的后缀是.lib
接下来使用一个故事来讲解一下动态库和静态库:
你是一名准高中生,但是你比较喜欢打CS,你在暑假的时候去找一个叫张三的学长请教怎样才能在学校里玩游戏,学长告诉你学校北门出门左转走200米有一家红蜻蜓网吧,那里有很多电脑,其中888号电脑网速最快还靠窗,非常棒,你就把这件事记在了心里。你上学后放周末了,你就开始列你这一天都要干什么(背单词、写作业、上网),你一件一件的去做,到点了你就去上网,然后就找到了张三学长告诉你的888号主机,你在那里玩得很开学,玩完就回来了,这样的日子一直持续到放寒假。你考试成绩很棒,你就和你老爸说,你要是没有跑来跑去的时间可能成绩还会更好,希望你老爸能和校长说一说,给你在宿舍安个电脑,你们校长看你成绩也不错就同意了,然后你就可以在宿舍玩游戏了。
上面的这个故事中,网吧就类似于动态库,你到玩的时候找到这家网吧,玩完了再回来,而你宿舍里的电脑就是静态库,你想多天玩就多天玩
所有动态库就是这个库放在公共的区间,谁想用都可以,而静态库就是你自己有一份,想什么时候用就什么时候用
3.2.2 动态链接和静态链接
所谓动态链接就是程序调用动态库进行链接,而静态链接也是同理
但是由于他们属性的不同会导致如下差异:
动态链接出的文件比较节省资源,体积较小,但是非常依赖库文件,静态链接出的文件浪费资源,体积较大,不依赖库文件,可以使用自己的
file 文件名可以查看文件的信息
那么我们如何实现静态链接呢?
指令:gcc 源文件 -o 目标文件名 -static
我们可以看到静态链接的程序大小远大于动态编译的程序大小