Gcc
程序编译过程
1. 预处理
在这一阶段,源码中所有的预处理语句得到处理,例如
#include语句所包含的文件内容替换掉语句本身
所有已定义的宏被展开
根据#ifdef #if等语句的条件是否成立取舍相应部分
gcc预处理阶段可以生成.i文件,
通过选项-E可以使编辑器在预处理结束时停止编译。例如:
gcc -E -o hello.i hello.c
2. 编译
在这一阶段,编译器对源码进行语法分析,优化等操作,最后生成汇编代码。
这是整个过程中最重要的一部。
可以通过选项-S使GCC在进行完编译后停止,生成.s的汇编程序。例如:
gcc -S -o hello.s hello.c
3. 汇编
这一阶段使用汇编器对汇编代码进行处理,生成机器语言代码,保存为后缀为.o的目标文件中。
当程序由多个代码文件构成时,每个文件都要进行汇编工作,生成.o目标文件后,才能进行下一步链接工作。
目标文件已经是最终程序的某一部分了,只是在链接之前还不能执行。可以通过-c选项生成目标文件:
gcc -c -o hello.o hello.c
4. 链接
经过汇编后的机器码不能直接运行,为使操作系统能正确加载可执行文件,
文件中必须包含固定格式的信息头。
还必须与系统提供的启动代码链接起来才能正常运行,这些工作都由链接器来完成。
gcc -o hello hello.c
Gcc基本用法
1. gcc [-Wall] [-O1..3] [-o name] file [-D 预先定义的变量][-L 目录][-lname][-fpic][-static/shared]
-Wall:打开所有警告项
-O 设置优化级别, O0表示关闭优化功能
-g 将调试信息编译到目标文件中
-o name:指定输出文件的名字是name
file:被编译(链接)的文件。
[-lname]:链接到libname.a或者libname.so
[-fpic] : 生成相对
2. 静态库
gcc -c add.c //编译 add.c 源文件生成 add.o 目标文件
ar crsv libadd.a add.o //对目标文件*.o 进行归档, 生成 lib*.a,
此处 lib 必须要写的
然后就是在编译其他文件时,链接静态库。
gcc -o mian main.c -L./ –ladd –I./
//不要忘记-L 后面的那个. (即在库文件的搜索路径中添加当前路径 -ladd 表示链接库文件 libadd.a/.so
-I./表示包含在当前目录中的头文件)
3. 动态库
gcc -fPIC -Wall -c add.c
gcc -shared -o libadd.so add.o
最后编译链接动态库: gcc -o main main.c -L. –ladd
在运行 main 前, 需要注册动态库的路径。常采用的方法有:
cp libadd.so /lib //通常采用的方法, cp lib*.so /lib 将其copy到系统的/lib文件夹下
创建动态链接库之后, 以后就可以使用该动态链接库了
例如在 test.c 里面调用了原来库中的函数, 则执行 gcc test.c –o test –ladd 就可以了。较方便
Gcc2
# 中间插入一段关于gcc 的前戏
gcc –E –o main.i mian.c # -E是预处理展开宏,生成详细c文件, -o是输出
gcc –S –o main.s main.i # -S 是编译阶段, 将c文件生成汇编语言
gcc –c –o main.o main.s # -c 是汇编阶段, 生成机器码
gcc –o main.exe main.o # 链接阶段, -o 生成目标执行程序
gcc –g # 编译中加入调试信息, 方便gdb调试, 还有-ggdb3 支持宏调试等
gcc –Wall # 输出所有警告信息
gcc –O2 # 开启调优, O2等级调优
gcc –I(i大写) # 导入头文件目录,方便 *.h文件查找
gcc –L(l 大写) # 导入库文件目录,方便 *.so和*.a文件查找
gcc –l(l 小写) # 导入库文件, 例如-lpthread, 相当于依次查找导入 libpthread.so/libpthread.a 文件
gcc –static –l(l 小写) # 指定只查找 静态库 lib*.a 文件, linux约定库文件都是 lib开头
ar rc libheoo.a hello.o world.o # 将*.o 文件打包成 libheoo.a 静态库
gcc –fPIC –shared –o libheoo.so hello.o world.o # 将*.o 文件打包成 libheoo.so 动态库
Makefile1
make和makefile
一个软件中通常包含很多文件,每次都进行编译会很麻烦而且效率低下。
在Windows平台上的VC、VB都有工程概念,一旦建立一个工程,这些开发平台会自动地维护其中的各种文件,从而高效编译。
在Linux中,使用make完成这一功能。
1. makefile文件说明
使用make工具,首先要编写makefile文件,一个makefile文件包含5个方面:
- 具体规则
- 隐含规则
- 定义变量
- 指令
- 注释
2. makefile的规则:
诸如Autoconf和Automake的工具可以自动生成makefile文件。
makefile的内容核心是一系列的规则。规则的基本格式是:
target:dependency(tab字符)command
- target(目标):通常是要产生的文件的名称
- dependency(依赖):指用来输入从而产生目标的文件。
- command(命令):是make执行的动作,一个规则可以有多条命令,每个命令一行。命令行首字符是TAB
3. makefile的实例:
objects = main.o mouse.o command.o display.o
CC = gcc
CFLAGS = -Wall -O2 -g //相当于C中定义变量
game : $(objects) //定义game生成规则
$(CC) -o edit $(objects)
main.o : defs.h //定义main.o生成规则,包含隐含规则
mouse.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
.PHONY : clean
clean : //定义clean的规则,无依赖
$(RM) edit $(objects)
game的生成规则包含了规则的三个基本要素:
目标、依赖、命令;
main.o等规则没有命令,属于隐含规则(implicit rule)。
clean规则没有依赖,不属于编译的内容,只是完成一个指定的动作。
隐含规则能够告诉make使用传统的标准方法完成任务。
例如,生成一个目标文件的方法是使用C编译器编译C语言源程序,
这个步骤所用到的命令基本都是相同的。
使用隐含规则就无须详细指定这些命令,
而make能按照文件名的后缀的变化,决定所采用的规则。
变量是makefile中定义的名字,用来代替一个文本中的字符串 ,该文本字符串称为该变量的值。
Makefile中常见预定义变量有:
(a)CC 默认值cc(gcc)是C编译器的名称。
(b)CPP 默认是$(CC) - E 是C与预编译器的名称。
(c)CXX默认值是g++ ,是C++ 编译器的名称、
(d)RM 默认值 rm - f 是删除程序的名称。
(f)$@ 用在生成规则中,表示当前目标。
(g)$< 用在生成规则中,表示当前目标的第一个依赖目标。
(h)$^ 用在生成规则中,表示当前目标的所有依赖目标。
使用变量的方式:$(变量名)变量名只是字符时,()可省略
常用的变量赋值操作:
:= 直接赋值,信值覆盖原来的值。
?= 条件赋值,如果原来无值则赋值。否则保持原来的值。
+ = 加法赋值,新值附加在原来值后面。
= 递归赋值,如果右侧包含其他变量,当这些变量的值变化时,被赋值变量的值也变化。
make 工具的基本用法如下:
make [ - C dir] [ - f file] [ target]
- C dir:执行时进入dir目录,默认当前目录
- f file:使用file作为makefile
target:要完成的目标,目标在makefile中定义,默认是定义的第一个目标。
Makefile2
target: dependency_files //目标项:依赖项
< TAB >command //必须以 tab 开头, command为编译命令
编写好Makefile之后,就可以直接使用 make 编译了。
若写了clean,可make clean 清理,重新编译。
如下:
特殊处理与伪目标:
. PHONY 是 Makefile 文件的关键字, 表示它后面列表中的目标均为伪目标。
伪目标通常用在清理文件、 强制重新编译等情况下。
eg:
. PHONY: clean
clean:
rm main main. o
变量、 函数与规则
通过make支持的变量定义、规则和内置函数,
可以写出通用性较强的makefile文件,
使得同一个makefile文件能够适应不能的项目是重要的。
变量: 用来代替一个文本字符串
变量名: = 变量值 简单变量展开( 类似于 C++ 的赋值)
使用变量的一般方法:
$( 变量名) = ? ? ? 赋值
? ? ? = $( 变量名) 引用
例如:-- 下
变量分为:
- 用户自定义变量,
- 预定义变量( CFLAGS),
- 自动变量,
- 环境变量
自动变量: 指在使用的时候, 自动用特定的值替换。
常见的有:
$@ 当前规则的目标文件( 重点)
$^ 当前规则的所有依赖文件, 以空格分隔( 重点)
eg: 用自动变量 CFLAGS: = - Wall - O2 –fpic
预定义变量:
内部事先定义好的变量, 但是它的值是固定的, 并且有些的值是为空的。
AR: 库文件打包程序默认为 ar
CC: c编译器默认为 cc
CPP: c预编译器, 默认为$( CC) –E
CFLAGS: c编译器选项, 无默认
CXXFLAGS: c++ 编译器选项
规则分为:
- 普通规则,
- 隐含规则,
- 模式规则
隐含规则:
* . o 文件自动依赖* . c 或* . cc 文件, 所以可以省略main. o: main. cpp 等
模式规则:
通过匹配模式找字符串,
% 匹配 1 或多个任意字符串
% . o: % . cpp 任何目标文件的依赖文件是与目标文件同名的并且扩展名为. cpp 的文件
函数:
1. wildcard 搜索当前目录下的文件名, 展开成一列所有符合由其参数描述的文件名,文件间以空格间隔。
SOURCES = $( wildcard * . cpp) 把当前目录下所有'.cpp' 文件存入变量 SOURCES 里。
2. 字符串替换函数:( patsubst 要查找的子串, 替换后的目标子串, 源字符串) 。
将源字符串( 以空格分隔) 中的所有要查找的子串替换成目标子串。 如
OBJS = $( patsubst % . cpp, % . o, $( SOURCES) ) 可以和上一步对应起来看。即把 SOURCES 中'.cpp' 替换为'.o' 。
3. ( addprefix 前缀, 源字符串) 函数把第二个参数列表的每一项前缀加上第一个参数值
Makefile举例:
SOURCES: = $( wildcard * . c)
OBJS: = $( patsubst % . c, % . o, $( SOURCES) )
ELF: = main
CC: = gcc
CFLAGS: = - g - Wall
$( ELF) : $( OBJS)
gcc $^ - o $@
. PHONY: clean
clean:
rm $( OBJS) $( ELF)
Makefile3
GDB
GDB是GNU的调试工具,它可以跟踪被调试的程序,进行设置断点、单步执行等操作。当程序暂停执行时,可以使用命令查看程序中的变量值、CPU的寄存器值、内存的值以及函数调用栈等信息。
被调试的应用程序在编译时最好使用-g参数将调试信息编入目标文件中:
gcc -g app.c -o app
用以下命令启动对程序app的调试:
gdb app
如果程序app运行时需要参数,则用以下命令:
gdb --args app arg1 arg2 // arg1和arg2被视为app的参数,而不是gdb的参数
启动后进入GDB交互界面,可以输入GDB的命令进行调试,常用命令如下(回车是重复上一条命令):
GDB2
gdb 常用命令
首先程序编译时加 -g 选项才可打开调试选项
eg:
gcc –o filename –Wall filename.c –g //进入调试
gdb filename //进入调试
l //显示代码 (list)
b 4 //在第四行设置断点 相当于 Windows 的 F9 (break)
//若为 b main 则表示断点打在main处
r //运行 相当于 Windows 的 F5 (run)
n //下一步不进入函数 相当于 Windows 的 F10 (next)
s //表示单步进入函数, 相当于 Windows 的 F11 (step)
p I //打印变量 I 相当于 Windows 的 Watch 窗口(print)
c //运行到最后(continue)
q //退出 相当于 Windows 的 Shift+F5 (quit)
参考
1. https://blog.csdn.net/iotflh/article/details/86499259
2. https://blog.csdn.net/smilejiasmile/article/details/74946733