C:编译流程
总阅读次
前言:
开放、自由和灵活是Linux的魅力所在,而在这一点在GCC上的体现就是程序员通过它能够更好的控制整个编译过程。在使用GCC编译程序时,编译过程可细分为4个阶段
- 预处理(Pre-Processing)
GCC在编译时首先要把头文件如
stdio.h
中内容加载到源程序首部。.i
已经预处理的C源程序(gcc -E hello.c hello.i
)visualstudio也是生成.i文件,不过要要在工程属性设置:
configuration Properties--->C/C++---->Preprocessor----右边选项----->PreProcess to a File
的选项“带行号(/P)”或者“不带行号(/EP /P),但是这样编译就不会生成.obj了
- 编译(Compiling)
必须实现3个步骤
词法分析:检查关键字、标示符是否正确;语法分析:检查程序中语法是否正确;
检查程序中逻辑语句是否正确
gcc -S hello
.s/.S
是汇编源程序visualstudio编译会生成
.obj
文件;VS也可设置生成汇编文件:
“Project” –> “xxx properties”(xxx表示但前的project名,也可以采用快捷键ALT+F7),选择左边窗口”Configuration properties”列表项的”C/C++”,接着选择”Output files”,最后在右边的窗口中的”Amssembler Output”选择”Assembly, Machine Code,and Source”.重新编译工程后可看到与.cpp同名的.cod文件.
汇编(Assembling)
汇编阶段任务是把汇编程序翻译成CPU可识别的二进制文件(又称为目标文件
.o
)gcc指令: gcc -c hello.s -o hello.o
链接(Linking)
目标文件虽然已经可以被CPU直接识别,但是单个目标文件一般是无法运行的,原因在于一个程序往往是由多个源文件组成,每个源文件只对应一个目标文件。
连接阶段的任务就是把程序的目标文件所需的问价连接在一起,最终生成一个可直接运行的文件,称为可执行文件。
gcc命令生成的可执行文件有3种:
a.out
(Assembler and Link editor output)、COFF
(Common object file format)、ELF
(Executable and linkable format),其中,a.out 和COF格式都是比较老的格式,现在Linux平台上可执行文件的主流格式是ELF。
Linux程序演可以根据自己的需要让GCC在编译的任何阶段结束,以便检查或使用编译器在该阶段的输出信息,或者对最后生成的二进制文件进行控制,以便通过加入不同数量和种类的调试代码来为今后的调试做好准备。和其他常用的编辑器一样,GCC也提供了灵活而强大的代码优化功能,利用它可生成执行效率更高的代码。
GCC编译器
GCC(GNU Compile Collection)是Linux平台最流行的编译系统。
- GCC可以为x86、ARM、MIPS扽各不同体系架构的硬件平台编译程序。
- GCC可以编译C、C++、Pascal、Java等数十种高级语言。
gcc命令常用选项以及工作流程
gcc命令的使用格式为:gcc [选项] [文件名] [选项] [文件名]
gcc常用命令
gcc命令拥有强大的编译选项,按类型可分为:
1. 总体选项
-c 对文件进行编译或汇编
-E
对源文件进行预处理-S
对源文件进行编译-o file
输出目标文件file-v
显示编译阶段命令
2. 语言选项
ansi
支持符合ANSI标准的C程序
3. 警告选项
-W
屏蔽所有的警告信息-Wall
显示所有类型的警告信息Werror
出现任何警告信息就停止编译
4. 调试选项
-g
产生调试信息
5. 优化选项:用于对目标进行优化,通常选项如下:
-O1
对目标文件的性能优化
-O2
在-O1de基础上进一步优化,提高目标文件的运行性
-O3
在-O2de基础上进一步优化,支持函数集成优化
-O0
不进行优化
6. 连接器选项:用于控制连接过程,常用选项如下:
static
使用静态连接library
链接library函数库文件Ldir
指定链接器的搜索目录dirLdir
指定搜索目录dir
库的使用
从逻辑上看,程序的主题是由一系列函数组成,所以编写程序的主要工作之一就是实现函数。
为了降低编程的工作量,碧昂成系统会把一些非常基本、常用的函数集中到函数库中实现,如
信息的打印函数
、文件的打开或关闭函数
、内存空间的申请与释放函数
、数学计算函数
;当程序需要使用到函数中某个函数时,就可以直接从库中调用。就好比建造房屋时,建筑队并不需要直接从头开始制造砖瓦和水泥,而只需要从原料市场购买就可以了。
每种高级编程语言都有各自的函数国库,例如,C语言的C库、Visual C++的MFC、Java的JFC等。函数库中的函数裤脚都是由经验丰富的资深程序员编写的;
函数库的使用不仅减少编程的工作量,还能有效提高程序的性能和健壮性。在面向对象编程中,函数库被封装在类中,所以含数据库就演变成了类库,但其原理和机制是类似的。
函数库的使用方式分为静态连接
和动态连接
2种
静态连接
是指编译系统在链接阶段把程序的目标文件和所需的函数库文件连接在一起,这样生成的可执行文件就可以在没有函数库的情况下运行。就好比火箭把燃料和氧料装载一起,就可以在没有空气的太空中飞行。
在使用静态链接方式产生的可执行文件体积较大,但运行效率较高。
动态链接
是指编译系统在连接阶段并不把目标文件和函数库文件链接在一起,而是等到程序在运行过程中需要使用时才链接函数库。使用动态链接方式产生的可执行文件由于没有库文件,所以体积小。但由于需要动态加载函数库,所以运行效率要低一点。
**Glibc(GNU Library C)**是GNU推出的C语言函数库,符合ISO C(International Standard for the C Programming Language)和POSIX(Portable Operating System Interface for Computer Environments)标准。其中,ISO C标准的扩充。因此Glibc可以在各种不同体系架构的计算机平台上使用。
Glibc中包含了大量的函数库,其中Libc是最基本的函数库,每个C程序都需要使用libc库。此外,常用的还有数学库libm、加密库libcrypt、POSIX线程库libpthread、网络服务库libnsl、IEEE浮点运算库libieee等。Glibc
库为C程序提供了大量功能的强大函数、日期时间函数等。
1 | C程序在调用Glibc中的函数库时,需要引用函数库对应的头文件,如stdio.h、string.h、time.h等。这些头文件都放在`/usr/include`目录下。同时,在编译命令中需要加入某些函数库的链接参数(在函数库的使用文档中会列出具体的链接库名称参数),并使用符号`-l`进行连接。比如`libm`库函数链接参数为m,libpthread库的链接参数为pthread等。例如: |
1 | root@ubuntu:~/test# gcc test.c -o test -lm |
在Linux系统中,Glibc分布在/lib和/usr/lib目录下,其中/lib目录中的函数库文件主要是给/bin目录下的系统程序使用的,/usr/bin目录中的函数库文件主要是给/usr目录下的用户程序使用的。
1 | libdialog.a |
其中后缀为.a
的是静态库文件、后缀为.la的是用来记录库文件信息的动态库文件,后缀为
.so的是动态库文件。其中
libspreadsheet-1.12.9.so是正真的Png动态库文件,而
libspreadsheet.so是指向
libspreadsheet-1.12.9.so`动态库文件的符号链接符号。
1 | root@ubuntu:/usr/lib# file libspreadsheet.so |
动态链接\静态链接对比
- 动态链接
使用动态连接方法编译程序时,动态库的符号链接文件会写入二进制文件中。这样程序运行时就可以通过符号链接文件找到指定的动态库文件了。
以gcc test.c -o test命令生成的可执行文件test,用file
查看test文件相关信息:
1 | root@ubuntu:~/test# file test |
dynamically linked(uses shared libs)
表明test文件使用了动态链接库。
1 | root@ubuntu:~/test# du -sh test |
其文件大小为12k
- 静态链接
用过参数static
对文件进行静态链接方式编译,生成test文件其中1
2root@ubuntu:~/test# file test
test: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=27c00935a81c38b2bd8a1a72a7037cbb15e88278, not strippedstatically linked
表明test文件使用了静态链接库。其文件大小也增大至856k1
2root@ubuntu:~/test# du -sh test
856K test