gcc编译器和库的概念
GCC编译器
gcc的编译流程:预处理、编译、汇编、链接
预处理阶段
将源代码预处理生成预处理文件(*.i),此阶段主要工作是处理源代码中的预处理指令,将头文件内容插入程序中,包括宏替换、条件编译等。
1 | gcc -E xxx.c -o xxx.i |
编译阶段
将预处理文件进行编译生成汇编文件(*.s),此阶段为核心阶段,主要工作为将源代码转化为汇编语言,这一过程会对源代码进行词法和语法分析,若存在语法错误会报错并停止编译。
1 | gcc -S xxx.i -o xxx.s |
汇编阶段
将汇编文件进行汇编处理生成目标文件(*.o),此阶段是将汇编语言转换为机器语言,并生成目标文件,其包括程序的可执行代码和程序运行所需的符号表、调试信息。
1 | gcc -c xxx.s -o xxx.o |
链接阶段
将目标文件进行链接生成可执行文件,此过程是将多个目标文件和相应的库文件进行链接生成可执行文件。
1 | gcc xxx.o -o xxx |
gcc常用选项
选项 | 说明 |
---|---|
-c | 只编译,不链接生成可执行文件 |
-o | 自定义输出文件名称,不能与源文件同名 |
-g | 产生符号调试工具 |
-O | 对程序进行优化编译,但其相应的编译链接速度慢些 |
-O2 | 比-O更好的优化编译,但整个过程速度会相对更慢 |
-I dirname | 将dirname所指出的目录加入到程序头文件目录列表中(预编译过程) |
-L dirname | 将dirname所指出的目录加入到程序函数档案文件和库文件的列表中(链接过程) |
条件编译
条件编译的作用:能让编译器在预处理阶段,选择性地将部分代码送到编译器进行编译,这样能按照条件省略部分无用代码,提高程序的可移植性和灵活性。使程序员能够更方便地根据不同需求和条件对代码进行选择性使用。
指令 | 说明 |
---|---|
#if | 判断是否满足条件,满足时执行这部分代码 |
#elif | 如果#if条件不满足,则执行这部分的代码 |
#else | 如果#if和#elif都不满足,则执行这部分代码 |
#ifdef | 判断是否定义了宏,如果是则执行该部分代码 |
#ifndef | 判断是否没有定义宏,若没有则执行该部分代码 |
#endif | 条件编译的结束语句 |
在使用条件编译判断宏是否定义时,编译时使用-D参数可添加宏定义
1 | gcc xxx.c -D _DEBUG_ |
makefile文件
make是一个命令工具,是一个解释makefile中指令的命令工具
makefile的作用是:用于管理和组织项目的编译过程,可以通过定义一系列的规则来指定源代码之间的依赖关系,它是一种自动化构建工具,可以帮助程序员进行自动化编译、链接和打包程序,能提高编译效率。
基本语法:
1 | target: prerequisites ... |
自定义变量:x=a,变量声明时需要给初值,取值时使用:$(x)或${x}
makefile变量的赋值
符号 | 作用 |
---|---|
= | 基本赋值 |
?= | 当变量已经定义赋值过了,则不执行赋值 |
:= | 覆盖式赋值 |
+= | 追加赋值 |
makefile自动变量
符号 | 作用 |
---|---|
$* | 不包含扩展名的目标文件名称 |
$+ | 所有的依赖文件,以空格分开,出现先后为序,可包含重复依赖文件 |
$< | 第一个依赖文件的名称 |
$? | 所有时间戳比目标文件晚的依赖文件 |
$@ | 目标文件的完整名称 |
$^ | 所有不重复的目标依赖文件 |
$% | 如果目标是归档成员,则该变量表示目标的归档 |
隐含变量
名称 | 含义 |
---|---|
AR | 库文件维护程序的名称,默认值为ar |
AS | 汇编程序的名称,默认值为as |
CC | C编译器的名称,默认值为gcc |
CPP | C预编译器的名称,默认值2为$(CC) -E |
CXX | C++编译器的名称,默认值为g++ |
FC | FORTRAN编译器的名称,默认值为f77 |
RM | 文件删除程序的名称,默认值为rm -f |
Makefile的条件判断
语句 | 说明 |
---|---|
ifeq | 判断是否相等 |
ifneq | 判断是否不相等 |
ifdef | 判断是否定义过 |
ifndef | 判断是否未定义过 |
endif | 以上语句的配套语句 |
Makefile函数
基本语法:
1 | $(<function> <arguments>) |
$(wildcard PATTERN)
功能:列出当前目录下所有符合”PATTERN”格式的文件名,其可使用shell可识别的通配符
返回:空格分隔,存在当前目录下符合的文件名
$(patsubst [pattern], [replacement], [text])
名称:模式字符串替换函数。
功能:查找[text]中的单词,以[replacement]的词进行替换。
1 | $(patsubst %.c, %.o, x.c.c bar.c) |
$(shell [命令])
功能:在makefile文件中,执行shell命令
返回:返回命令执行的结果
$(call 自定义函数)
功能:call命令可以执行自定义的函数
1 | define Myfunc |
make的使用
常用的参数如下:
参数 | 说明 |
---|---|
-C | dir读入指定目录的Makefile |
-f | file读入当前目录下的file文件作为Makefile |
-i | 忽略所有命令执行错误 |
-n | 只打印要执行的命令,但不执行这些命令 |
-s | 执行命令时不显示命令 |
-w | 如果make在执行过程中改变目录,打印当前目录名 |
库的概念
库是一个二进制文件,包含的代码可以被程序调用,是编译好的,可复用的代码,Windows和Linux下的库文件格式不兼容,Linux下包含静态库和共享库。
静态库特点
编译(链接)时把静态库相关的代码复制到可执行文件中,程序已包含静态库,运行时不需要静态库
程序运行时无需加载库,运行速度快,但占用更多的磁盘和内存空间,静态库升级后,程序需要重新编译链接
创建静态库文件 xxx
1 | gcc -c xxx.c |
ar命令的参数
- c:禁止在创建库时产生的正常消息
- r:如果指定的文件已经存在于库中,则替换它
- s:强制重新生成库符号表
- v:将建立新库中的详细的逐个文件的描述写至标准输出
- q:将指定文件添加到库的末尾
- t:将库的目录写至标准输出
注意:静态库名字要以lib开头,后缀名为 .a,没有main函数的.c文件无法生成可执行文件
链接静态库
1 | gcc -o test main.c -L. -lxxx |
动态库特点
编译(链接)时仅仅记录用到哪个共享库中的哪个符号,不复制共享库中的相关代码,程序占用空间小,且多个程序可使用同一个共享库,程序运行时需要加载库文件,库的升级比较方便、无需重新编译程序,使用广泛
创建动态库
1 | gcc -c -fPIC xxx.c xxxx.c // 生成与位置无关的目标文件 |
链接动态库
1 | gcc -o test main.c -L. -lxxx |
在生成动态库后,需要将库文件告诉系统文件位置
方法一:将库文件拷贝到/usr/lib和/lib中
方法二:在LD_LIBRARY_PATH环境变量中添加库文件的所在路径
方法三:添加/etc/ld.so.conf.d/*.conf文件,执行ldconfig刷新
nm命令:查看链接文件的内容
ldd命令:查看链接库的路径