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
2
target: prerequisites ...
command

自定义变量: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
2
3
$(<function> <arguments>)

${<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
2
3
4
5
define Myfunc
$(0)
endef

$(call Myfunc, 10)

make的使用

常用的参数如下:

参数 说明
-C dir读入指定目录的Makefile
-f file读入当前目录下的file文件作为Makefile
-i 忽略所有命令执行错误
-n 只打印要执行的命令,但不执行这些命令
-s 执行命令时不显示命令
-w 如果make在执行过程中改变目录,打印当前目录名

库的概念

库是一个二进制文件,包含的代码可以被程序调用,是编译好的,可复用的代码,Windows和Linux下的库文件格式不兼容,Linux下包含静态库和共享库。

静态库特点

编译(链接)时把静态库相关的代码复制到可执行文件中,程序已包含静态库,运行时不需要静态库
程序运行时无需加载库,运行速度快,但占用更多的磁盘和内存空间,静态库升级后,程序需要重新编译链接

创建静态库文件 xxx

1
2
gcc -c xxx.c
ar -rsv libxxx.a xxx.o

ar命令的参数

  • c:禁止在创建库时产生的正常消息
  • r:如果指定的文件已经存在于库中,则替换它
  • s:强制重新生成库符号表
  • v:将建立新库中的详细的逐个文件的描述写至标准输出
  • q:将指定文件添加到库的末尾
  • t:将库的目录写至标准输出

注意:静态库名字要以lib开头,后缀名为 .a,没有main函数的.c文件无法生成可执行文件

链接静态库

1
gcc -o test main.c -L. -lxxx

动态库特点

编译(链接)时仅仅记录用到哪个共享库中的哪个符号,不复制共享库中的相关代码,程序占用空间小,且多个程序可使用同一个共享库,程序运行时需要加载库文件,库的升级比较方便、无需重新编译程序,使用广泛

创建动态库

1
2
gcc -c -fPIC xxx.c xxxx.c	// 生成与位置无关的目标文件
gcc -shared -o libxxx.so // 生成动态库文件

链接动态库

1
gcc -o test main.c -L. -lxxx

在生成动态库后,需要将库文件告诉系统文件位置

  • 方法一:将库文件拷贝到/usr/lib和/lib中

  • 方法二:在LD_LIBRARY_PATH环境变量中添加库文件的所在路径

  • 方法三:添加/etc/ld.so.conf.d/*.conf文件,执行ldconfig刷新

nm命令:查看链接文件的内容

ldd命令:查看链接库的路径