本文的示例代码
文件层级:
其中,test.c代码:
// test.c
#include <stdio.h>
#include "mymath.h"// 自定义头文件
int main(){
int a = 2;
int b = 3;
int sum = xkadd(a, b);
printf("a=%d, b=%d, a+b=%d\n", a, b, sum);
}
mymath.h中声明相关方法:
// mymath.h
#ifndef MYMATH_H
#define MYMATH_H
int xkadd(int a, int b);
int xksum(int a, int b);
#endif
mymath.c中实现相关方法:
// mymath.c
int xkadd(int a, int b){
return a+b;
}
int xksum(int a, int b){
return a-b;
}
编译的整个过程
预处理:在编译之前由预处理器对源码进行的处理。主要包括宏替换、删除注释、执行预处理命令入#include、#ifdef等,生成.i文件。预处理后的文件会比源文件大很多,因为#include执行了复制,就<stdio.h>这个文件就有很多行了。此时文件仍然可以以文稿形式打开。
编译:将源码转化成汇编代码生成.s文件。汇编之后的.s文件都是汇编指令,比如movl、call等。此时文件仍然可以以文稿形式打开。
汇编:将汇编语言转化成机器码,生成.o文件。生成.o文件就是机器码了,不能使用文稿打开。
链接:编译之后的文件还不能直接执行,因为一个项目中存在多个文件,文件之间有引用关系,将这些文件进行关联并最终生成可执行程序的过程就是链接。
备注
宏义上的编译是指程序从源文件到二进制程序的全部过程,该过程包含预处理、编译、汇编、链接三个阶段。或者是从源文件到二进制文件的全过程,也就是生成.o文件的过程,该过程包含预处理、编译、汇编三个阶段。狭隘的编译就只是将.i文件转化成.s文件的过程。通常我们所说的编译就是宏义上的编译。
整个过程的流程图:
Compile Step
备注
有的博客中指出链接后有三种结果,分别是静态库、动态库、可执行文件。这种说法是错误的,动态库是通过gcc编译指令在链接之前生成,静态库的生成是通过ar指令进行打包,所以库是链接过程的参与文件而不是产物。
gcc指令
基本格式:gcc [options] file1 file2... // 不加入参数时,默认依次执行编译、汇编、链接操作,并生成名为 a.out的可执行文件
常用参数:-E // 只执行预处理操作,生成.i文件
-S // 只执行到编译操作为止,生成的是汇编文件(.s 或 .asm)
-c // 只执行到汇编为止,不进行链接,生成.o的机器码文件
-o filename // 指定输出地址和文件名
-static // 静态链接
-share // 动态链接,也叫共享链接
-g // 编译时生成debug有关的程序信息(供gdb使用)
--save-temps // 保留中间文件,比如.i、.s、.o文件等
-I PATH // 在指定目录寻找引用文件
-lname // 链接库,其中name为去掉lib前缀和.so/.a后缀后的名字,-l后面不加空格,如-lmysqlclient就是链接libmysqlclient.a的静态库
-L PATH // 优先在指定路径下搜索链接的库文件,不设置时按照默认顺序寻址链接库
完整示例:
gcc –L /usr/dev/mysql/lib –static –lmysqlclient test.o –o test
拆解:
-o:将test.o文件以test文件名生成在当前路径
-L:在/usr/dev/mysql/lib下寻找相关的库文件
-l:mysqlclient在对应的路径下有libmysqlclient.so和libmysqlclient.a,使用-l(小写)指令时去掉lib和.so/.a后缀
–static:默认情况下, GCC在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,加上-static选项,强制使用静态链接库。
静态库链接时搜索路径顺序:
- ld会去找GCC命令中的参数-L
- 再找gcc的环境变量LIBRARY_PATH
- 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的
动态链接时、执行时搜索路径顺序:
- 编译目标代码时指定的动态库搜索路径
- 环境变量LD_LIBRARY_PATH指定的动态库搜索路径
- 配置文件/etc/ld.so.conf中指定的动态库搜索路径
- 默认的动态库搜索路径/lib
- 默认的动态库搜索路径/usr/lib
有关环境变量:
LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径
LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径
更多动态库、静态库知识请看:C语言底层原理(二):动态库、静态库
多文件编译
多文件编译的集中方法:
1、将多个文件编译成.o文件之后统一进行链接,并在链接时指定文件地址,示例:
文件夹层级:
指令:
gcc test.o ./inc/mymath.o -o a.out
2、将多个源文件直接使用gcc指令直接生成可执行程序
以本文最初提到的文件层级来执行以下指令:
执行指令:
gcc test.c ./inc/mymath.c -I ./inc -o a.out
整个cmd过程:
caoxkdembp:C-Compile caoxk$ gcc test.c ./inc/mymath.c -o a.out
test.c:4:10: fatal error: 'mymath.h' file not found
#include "mymath.h"// 自定义头文件
^~~~~~~~~~
1 error generated.
caoxkdembp:C-Compile caoxk$ gcc test.c ./inc/mymath.c -I ./inc -o a.out
caoxkdembp:C-Compile caoxk$ ./a.out
a=2, b=3, a+b=5
caoxkdembp:C-Compile caoxk$
3、如果要编译的文件都在同一个目录下,可以用通配符gcc *.c -o 来进行编译
makefile:文件描述了整个工程的编译、连接等规则。对于大型项目而言,可以编写makefile来实现自动编译整个项目中的文件,具体可以了解makefile的编写规则,此处不涉及。
更多文章
网友评论