1. 简朴先容
GCC(GNU Compiler Collection)是一个广泛利用的编译器集合,支持多种编程语言,包括C、C++、Objective-C、Fortran、Ada和Go等。
GCC中的C编译器通常被称为gcc,而C++编译器则被称为g++。
gcc和g++的区别
- gcc:紧张用于编译C语言代码。对于文件扩展名为.c的源文件,gcc会将它们作为C代码处置惩罚。
- g++:紧张用于编译C++语言代码。对于文件扩展名为.cpp、.cc、.cxx或.C的源文件,g++会将它们作为C++代码处置惩罚。
gcc和g++都是GNU编译器集合的一部分,但g++是专门用于C++编译的,而gcc则是一个更通用的编译器,可以处置惩罚C、C++在内的多种语言。在编译C++代码时,利用g++可以避免手动链接C++库的麻烦,并且能够确保C++特性的正确处置惩罚。
gcc与g++的利用方式非常相似,接下来我们紧张以gcc编译C语言代码为例先容。
2. 编译源代码
实行下面的指令即可利用gcc编译源代码,生成可实行程序或中间文件:
- gcc -o [目标文件] [编译选项] [要编译的文件]
- gcc [编译选项] [要编译的文件] -o [目标文件]
复制代码 [-o]选项用于指定要生成的文件的名称,若未指定则会发生默认行为,这个我们在下文中再进行讲解;[选项]用于选择将要编译的文件编译到哪个阶段。
2.1 编译的四个阶段
编译实际上会经历四个阶段:预处置惩罚、编译(整个编译过程中的一步)、汇编、链接。
在进行前三个阶段的过程中,每个阶段结束都会产生一个中间文件,以便下一个阶段接手该阶段的工作:
• 预处置惩罚(进行宏更换/去注释/条件编译/头文件展开等)
[.c文件] -> [.i文件]
• 编译(生成汇编代码)
[.i文件] -> [.s文件]
• 汇编(生成二进制机器码)
[.s文件] -> [.o文件]
• 链接(将各个[.o文件]及相关库进行链接,生成可实行程序或库文件)
[.o文件] -> [可实行程序或库文件]
可通过下面的选项控制实行到哪个阶段:
• [-E]:到预处置惩罚阶段完成为止
[未完成预处置惩罚的文件] -> [.i文件](不指定目标文件时输出到控制台)
• [-S]:到编译阶段完成为止
[未完成编译的文件] -> [.s文件](不指定目标文件时输出到控制台)
• [-c]:到汇编阶段完成为止
[未完成汇编的文件] -> [.o文件](不指定目标文件时输出到[同名.o文件])
• 不加选项:到链接阶段完成为止(结束)
[未完成链接的文件] -> [可实行程序或库文件](不指定目标文件时输出到[a.out文件])
2.1.1 预处置惩罚
- gcc -E [未完成预处理的文件] -o [.i文件]
复制代码 在该阶段进行宏更换/去注释/条件编译/头文件展开等:
宏更换:将全部的宏定义用它们的值更换。
文件包含:将#include指令引入的头文件内容直接插入到指令所在的位置。
条件编译:根据条件编译指令(如#if、#ifdef、#ifndef等)的判断,选择性地包含或清除代码块。
删除注释:去除源代码中的全部注释(//和/* */),以便在后续阶段不会被处置惩罚。
2.1.2 编译
- gcc -S [未完成编译的文件] -o [.s文件]
复制代码 在该阶段生成汇编代码或目标文件,这个阶段(狭义编译)是整个编译过程(广义编译)最核心的部分,包括对代码进行词法分析,语法分析,语义分析,生成中间代码,中间代码优化等阶段。
有一门课叫做<<编译原理>>(极难),对该阶段进行了详细的先容,这个阶段不是一篇小小的博文能够讲清楚的,我就不作过多先容了。
留意,说是生成的汇编代码,但实际上又不完全是,只是很相似。
2.1.3 汇编
- gcc -c [未完成汇编的文件] -o [.o文件]
复制代码 该阶段讲编译阶段生成的汇编代码翻译为可被机器辨认并实行的二进制机器码。
也就是说[.o文件]已经可以被机器实行了,但是此中对库的调用,以及对其他文件中定义的函数/范例等的调用并不能完成,因为其还没有与这些文件创建接洽。
我们也称这个[.o文件]依赖于其他的[.o文件]或库文件。
2.1.4 链接
- gcc [未完成链接的文件] -o [可执行程序或库文件]
复制代码 这个阶段就是将各个[.o文件]以及库文件进行链接,满足他们之间的依赖关系。
链接的方式有两种:动态链接和静态链接。
2.2 其他选项
下面的选项独立于[-o]选项和编译选项存在,可进一步控制整个编译过程的细节:
• [-static]:此选项对生成的文件采用静态链接。
• [-g]:生成调试信息。GNU 调试器可利用该信息。
• [-shared]:此选项将只管利用动态库,以是生成文件比较小,但是必要体系有动态库。 • [-O0]、[-O1]、[-O2]、[-O3]:编译器的优化选项的4个级别,[-O0]表现没有优化,[-O1]为缺省值,[-O3]优化级别最高。
• [-w] 不生成任何告诫信息。
• [-Wall] 生玉成部告诫信息。
3. 动态链接和静态链接
3.1 静态链接
静态链接是在编译时将程序所需的全部库代码链接到单个可实行文件的过程。
这意味着整个库代码成为可实行文件的一部分。当程序运行时,它不必要单独访问库代码,因为它已经存在于可实行文件中。这使得可实行文件变大了,但它可以在任何体系上运行,而不必要单独安装库。静态链接通常用于小程序或库代码不太可能经常更改的情况。
优点:
在可实行程序中已经具备了实行程序所必要的全部东西,在实行的时候运行速率快。
缺点:
- 节浪费空间:因为每个可实行程序中对全部必要的目标文件都要有一份副本,以是如果多个程序对同一个目标文件都有依赖,如多个程序中都调用了printf()函数,则这多个程序中都含有printf.o,程序在运行时printf.o会被多次加载到内存中。
- 更新比较困难:每当库函数的代码修改了,这个时候就必要重新进行编译链接形成可实行程序。
3.2 动态链接
动态链接是在运行时将库代码链接到可实行文件的过程。
这意味着库代码不是可实行文件的一部分,而是作为共享库文件单独存储的(Windows上是.dll,Linux上是.so)。当程序运行时,它在实行过程中动态地访问共享库文件。这导致可实行文件更小,因为库代码没有包含在可实行文件中,但它还要求库文件必须安装在运行程序的体系上。动态链接通常用于大型程序或库代码可能经常更改的情况。
优点:
- 节省内存和磁盘空间:由于多个程序可以共享同一个动态链接库,因此避免了在每个程序中重复包含相同的代码和数据,从而节省了内存和磁盘空间。
- 便于更新和维护:当动态链接库更新时,只必要更换掉旧的库文件,而不必要重新编译或链接依赖于该库的程序。这大大进步了软件的更新和维护效率
缺点:
- 启动时间可能较长:由于动态链接必要在程序运行时加载和剖析库文件,因此可能会增长程序的启动时间。特别是当程序依赖于多个大型库时,这个问题会更加明显。
- 存在版本兼容性问题:如果新版本的动态链接库与旧版本的程序不兼容,那么程序可能会出现运行错误或崩溃的情况。别的,如果程序依赖于多个版本的库文件,还可能会出现DLL地狱(DLL Hell)问题。
3.3 操纵体系中的库
• Linux下,动态库XXX.so,静态库XXX.a
• Windows下,动态库XXX.dll,静态库XXX.lib
动态库一般会存放到操纵体系指定的目录下,当程序必要链接到对应的库时,体系会到该目录下查找,不同的操纵体系有所不同。
利用[ldd]指令可以查看可实行程序依赖的动态库,利用[file]指令可以验证程序利用的链接方式是动态链接还是静态链接:
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |