编译过程
- 1 预处理(进行宏替换)
- 2 编译(生成汇编)
- 3 汇编(生成机器可识别的代码)
- 4 连接 (生成可执行文件或库文件)
gcc 基本格式
gcc 选项 要编译的文件 选项 目标文件
gcc选项
- E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
- -S 编译到汇编语言不进行汇编和链接
- -c 编译到目标代码
- -o 文件输出到 文件
预处理(进行宏替换)
- 预处理功能主要包括宏定义,文件包含,条件编译,去注释等
- 预处理指令是以#号开头的代码行
例: gcc -E hello.c -o hello.i
-E :让gcc在预处理结束后停止,不进行编译过程
-o :表示我们要求输出的可执行文件名。 i后缀表示 已经预处理过的c原始程序
编译(生成汇编)
- 检测代码的规范性 是否有语法错误 检查无误后 gcc把代码翻译成汇编语言
- 用户可以使用 ‘ -S ’选项来进行查看 该选项只进行编译而不进行汇编,生成汇编代码
- 实例: gcc -S hello.i -o hello.s
汇编(生成二进制代码)
- 汇编阶段是把编译阶段生成的“.s”文件转成目标文件
- 在此可使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了
实例: gcc –c hello.s 系统会默认生成 hello.o
如果gcc –c hello.s –o(文件名) 添加 -o可以指定生成文件名 但文件内容一样
与前面的-E 预处理 和-s 生成汇编
连接(生成可执行文件或库文件)
- 成功编译之后进入链接阶段
实例: gcc hello.o –o hello.c 此时的hello.c文件变成可执行文件
库函数
在c语言程序中,并没有printf函数的定义,stdio.h中也只有该函数的声明,没有该函数的实现。系统把这些函数的实现放到指定的库文件中例如 libc.so.6,gcc会到系统默认的路径中进行查找,这就是连接的作用
静态库函数
- 静态库,是指编译链接时,把汇编生成的**.o**文件(二进制文件)和引用的静态库中的函数(二进制形式)打包放到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了,所以当程序生成后,即使删除静态库函数,程序依然可以运行。静态后缀名一般为“.a”(windows下为.lib)。
优点: - 优点:提高程序运行效率,生成可执行文件后,可删除静态库
- 缺点:占用磁盘和内存空间 静态库会被添加到和它连接的每个程序中, 而且这些程序运行时, 都会被加载到内存中.不方便库的修改 需要从新编译库函数 让后在打包.o文件
建立方法:
- 将包包含头文件.h(函数的定义)的.c(函数声明)文件进行汇编
例如: gcc -c add.c 默认会生成.o文件 也可以 -o 设定生成文件名 - 生成静态库 ar -rc lib库名称.a add.o
ar是归档命令 rc replace and create
注意:默认生成的静态库文件名称由lib开头.a 结尾,中间是库名称,在使用静态库的时候直接用库名称即可 - 使用
例如: gcc main.c -L. -l库名称 -o main
-L 后面跟指定的库路径
-l 后面跟静态库名
动态库函数
- 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是放入需要函数的引用表,在程序运行时,需要的函数代码被拷贝到内存中,进一步操作系统使用虚拟内存,每一份动态库都可以被多个程序使用,节省内存,不过由于运行时要去连接库会花费时间。
- 优点:节省内存 修改库函数方便
- 缺点:速度相对叫较慢
- 动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态库。
- gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文
- gcc默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证
建立方法
- 将包包含头文件.h(函数的定义)的.c(函数声明)文件进行汇编 注意需要加-fPIC 表示产生与位置无关代码(Position-Independent Code),没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
- 生成动态库 gcc -shared -o lib库名称.so add.o
shared表示生成共享库格式 默认 lib开头.so结尾 - 使用:方法一 拷贝.so文件到系统共享库路径下, 一般指/usr/lib
方法二 更改LD_LIBRARY_PATH = . 完成后和静态库使用方法一样