深入篇【Linux】学习必备:谈gcc/g++是如何完成程序的翻译过程&&动静态库介绍&&debug与release模式
- Ⅰ.gcc/g++是如何完成程序的翻译过程的?
- ①.预处理(宏替换)
- ②.编译(生成汇编)
- ③.汇编(生成机器可识别代码)
- ④.链接(生成可执行文件)
- Ⅱ.动态库与静态库
- ①.动态链接
- ②.静态链接
- ③.总结:
- Ⅲ.debug模式与release模式
Ⅰ.gcc/g++是如何完成程序的翻译过程的?
在Linux中使用gcc进行编译使用格式:
1.gcc[选项] 要编译的文件 [选项] 目标文件
2.gcc[选项] 要编译的文件
第二种方式直接生成可执行文件a.out
第一种方式可以生成可执行文件并且可重命名。
要完成程序的翻译要经过4个过程:预处理,编译,汇编,链接。
①.预处理(宏替换)
1.预处理阶段主要功能是头文件展开,宏替换,去注释,条件编译。
2.预处理指令是以#为开头的命令
3.可以通过选项-E来查看预处理阶段的情况,选项-E的功能就是让程序在完成预处理后就停止,不要再进行编译了。
4.实例:gcc -E test.c -o test.i
5.选项-o是指生成目标文件,最后生成一个.i文件。
②.编译(生成汇编)
1.编译阶段,gcc首先会检查代码的规范性,语法是否有错误,待检查完毕,会将test.i文件中的代码转化为汇编语言。
2.可以通过选项-S来查看编译阶段的情况,选项-S的功能就是让程序在完成编译阶段后就停止,不要再进行汇编了。
3.实例:gcc -S test.i -o test.s
4.选项-o生成目标文件,最后生成一个.o文件
③.汇编(生成机器可识别代码)
1.汇编阶段,gcc会将汇编语言转化为二进制机器语言。最后会将’.s’文件转化生成一个’.o’目标文件
2.可以通过选项-c来查看汇编代码已转化成二进制目标代码。
3.实例:gcc -c test.s -o test.o
4.最终生成一个可重定位目标二进制文件,简称目标文件。
④.链接(生成可执行文件)
1.链接阶段主要功能是将目标文件链接生成可执行文件。
2.实例:gcc test.o -o test
3.最终生成了可执行文件test
你们知道printf函数是怎么使用的呢?函数的使用需要什么呢?
printf函数的使用分为三部分,第一部分就是函数声明,第二部分就是函数功能的实现,第三部分就是函数的调用了。
第一部分和第三部分我们都已经实现了,函数声明在头文件里,而预编译阶段就将头文件展开了,所以函数的声明自然有了,而在我们的代码中直接调用printf函数也实现了,那么问题来了,第二部分在哪实现的呢?
其实在链接过程中,链接的其实就是库。库里给我们提供各种函数方法的实现。
所以,头文件提供方法的声明+库文件提供的方法的实现+你的代码=你的软件。
而库文件又分为动态库和静态库,进而链接阶段又有动态链接和静态链接。
Ⅱ.动态库与静态库
问题:在C程序中我们在使用printf函数时,并没有定义函数的实现,而在预处理阶段只是将头文件展开,头文件里也只是有函数的声明,并没有函数的定义。那么到底是在哪里定义printf函数的呢?
答案:系统把这些函数的实现都放到名为lib.so.6的库文件中去了。在没有特别指定时,gcc会默认到/usr/lib路径下进行查找,也就是会链接到lib.so.6这个库文件。从而可以实现printf函数了。而链接过程就是将目标文件和库文件进行链接生成可执行程序。
其实库本质上就是一个源文件,经过一定翻译,只给提供一个文件即可,不用给你提供太多的源文件,也可以达到隐藏源文件的目的。它是有路径的。
库也有自己的命名规则,动态库的命名规则一般是:lib name.so.xxx
并且我们的机器上一般只有动态库,没有静态库,需要自己下载静态库。
下载C++的静态库
sudo yum intsall -y libstdc++-static
下载C的静态库
sudo yum install -y glibc-static
那我们的目标文件是如何跟库链接的呢?
链接过程又分为动态链接和静态链接。
①.动态链接
动态链接是指在链接过程中,没有将库文件中的代码拷贝到可执行文件中,而是在程序执行时由运行时链接文件加载库,也就是跳转到动态库文件里,然后再出来。这样可以减少系统开销。动态库一般后缀为.so 前面的lib.so.6就是动态库。gcc在链接时,会自动默认链接动态库,也就是进行动态链接。
1.动态库又称为共享库,每个可执行文件都可以使用。
2.动态库是不能缺失的,一旦对应的动态库缺失,就无法链接了,不仅这个可执行文件无法链接,其他可执行文件都无法链接了。
3.ldd命令可以查看生成的可执行程序是依赖动态库还是静态库的。
4.file命令可以查看生成的可执行程序是动态链接还是静态链接
②.静态链接
静态链接指在链接过程中,将静态库中的代码全部拷贝到可执行文件中,所以生成的文件比较大,但在运行时就不再需要库文件了。其后缀一般为’.a’
1.编译器在使用京要哭进行静态链接时,会将自己的方法拷贝到可执行文件中,然后改文件就不需要再依赖库文件了,可以随意执行起来。
2.在Linux中如果想要让程序进行静态链接,需要在链接过程后面加上-static
实例:gcc test.o -o test_static -static
3.ldd命令可以查看生成的可执行程序是依赖动态库还是静态库的。
4.file命令可以查看生成的可执行程序是动态链接还是静态链接
5.静态链接生成的可执行文件要比动态链接生成的可执行文件大,因为静态链接过程中,静态库将方法拷贝到可执行文件中,增大了文件的大小。
6.如果没有静态库,在链接过程后面加-static。这样是不行的。
7.如果没有动态库,但有静态库,并且gcc可以找到。那么当我们进行连接时,gcc默认会先链接动态库,但没有动态库,所以只能链接静态库。所以我们可以发现选项-static是改变了链接的优先级。
8.一个程序不一定全部都是静态链接或动态链接的。通常是混合链接。
③.总结:
动态库和静态库各有什么优缺点呢?
1.动态库:
【优点】动态库因为是共享库,可以有效的节省资源(节省磁盘空间,节省内存空间,节省网络空间)。
【缺点】动态库一旦缺失,会导致其他程序无法正常运行。
2.静态库:
【优点】静态库可以使程序执行时不依赖库,程序可以独立运行。
【缺点】生成的可执行文件体积太大,消耗资源。
Ⅲ.debug模式与release模式
1.程序的发布有两种方式,一种是debug模式,一种是release模式。
2.Linux gcc/g++生成的可执行程序默认是release默认。
3.debug模式发布的程序是可以追踪调试的。
4.要想以debug模式发布程序需要在生成可执行程序时在后面加上-g选项。5.【问题】为什么debug模式发布的程序是可以追踪调试的? 因为在形成可执行程序时候,程序中添加了debug信息。 那么我们可以知道,debug模式发布的程序的大小一定会比release模式发布的程序大小要大,因为要包含debug信息。
6.readelf -S 是用来读取可执行程序中二进制的构成的。我们可以用该命令来查看添加的debug信息
利用管道来查找test_debug可执行程序中的含debug的信息
readelf -S test_debug |grep -i debug