文章目录
1. gdb调试
1.1 使用场景
-
程序编译无误,但是有逻辑错误
-
使用文字终端(shell),实现一个单步调试的功能
-
生成可执行文件之前必须加参数 -g
gcc hello.c -o hello -g
1.2 启动调试
-
gdb + 可执行文件(如:gdb hello)
1.3 查看代码
-
l – list
-
l: 默认打开main函数所在文件,显示前10行 -
l n[或函数名]: 显示当前文件的第n行 [ 或某个函数 ]- 如果未打开其他文件,则默认当前文件为main函数所在文件
-
l filename:n[或函数名]: 显示文件filename的第n行 [ 或某个函数 ]- 按
Enter(默认执行上次操作)可以一直显示下去直到文件末尾
- 按
1.4 设置断点
-
b – break
-
设置当前文件断点:
b 行号 [ 或函数名 ]
-
设置指定文件断点:
b fileName:行号 [ 或函数名 ]
-
设置条件断点:
b 10 if value==19- 当value=19时,停止
-
查看断点号:
-
info break[i b]
-
-
删除指定断点号对应的断点:
d(delete/del)+ 断点号
-
设置断点是否有效
disable/enable + 断点号
1.5 开始执行gdb调试
-
r(run)--> 运行程序 -
start--> 单步执行,运行程序,停在第一行执行语句 -
n(next)--> 下一行(不会进入到函数体内部) -
s(step)--> 下一步(会进入到函数体内部) -
c(continue)--> 直接停在断点的位置 -
finish--> 结束当前函数,返回到函数调用点- 注意:必须当前函数的循环中没有断点了,否则会失败
1.6 查看(打印)变量的值
p(print) + 变量名
1.7 查看变量的类型
ptyte + 变量名
1.8 设置变量的值
set var 变量名 = 赋值- 作用:比如可以通过给循环控制变量i设置某个值,即可查看循环在i为某个值的执行状态
1.9 设置跟踪变量
-
追踪变量:
display + 变量名- 之后每执行一步,该变量值都会被打印出来
-
取消跟踪:
undisplay + 变量名编号
1.10 退出gdb调试
q(quit)
2. makefile的编写
本部分内容只是最基础的makefile的知识,如果有扩展学习的需要,可以参考徐海兵老师翻译整理的GNU make中文手册点此下载
2.1 文件的命名规则
- Makefile
- makefile
2.2 用途
- 项目代码编译管理
- 节省编译项目的时间
- 一次编写终身受益
2.3 基本规则
-
三要素: 目标、依赖、命令
-
格式:
- 目标 --> 要生成的目标文件
- 依赖 --> 生成目标文件需要的一些文件
- 命令 --> 借助依赖文件生成目标文件的手段
- tab --> 缩进,有且只有一个
-
其他语法:
-
#是注释 -
.PHONY定义伪目标- 例子:
- 例子:
-
执行伪目标:
make clear或make clearall
-
-
Makefile会把规则中的第一个目标作为终极目标
- app:all --> all指定生成的最终目标为app
- app:all --> all指定生成的最终目标为app
2.4 工作原理
-
若想生成目标,检查规则中的依赖条件是否存在,如果不存在,寻找是否有规则用来生成该依赖文件
-
检查规则中的目标是否需要更新,必须检查它的所有依赖,依赖中有任意一个被更新,则目标必须更新
- 依赖文件比目标文件时间晚,则需要更新
-
生成终极目标的过程:
-
更新目标的工作原理:
- 检查目标和依赖的修改时间,若依赖比目标新,则执行命令更新目标文件
- 检查目标和依赖的修改时间,若依赖比目标新,则执行命令更新目标文件
2.5 执行
-
make--> 通过makefile生成终极目标- 直接 make,使用makefile文件
-
make -f mm- 指定一个名字不为makefile的文件
-
make clean--> 清除编译生成的中间.o文件和最终目标文件-
不生成终极目标,而执行makefile中的目标对应命令
-
如果当前目录下有同名clean文件,则不执行clean对应的命令
-
-
因为在执行make clean时,并不真正生成clean文件,由更新目标的工作原理可知,假如已经存在最新的目标文件,则不执行其命令,因此一旦目录下有同名的clean文件,那么在执行make clean时便会认为已经存在最新的目标文件,而不执行相应命令。
-
上述问题解决方案 --> 伪目标声明:
.PHONY:clean -
特殊符号
-
- 命令:表示此条命令出错,make也会继续执行后续的命令。 -
e.g.
-rm a.o b.o如果删除失败,则继续向下执行其他命令
-
-
2.6 普通变量
-
普通变量
- 定义变量:变量 = 是替换 变量 += 是追加 变量 := 是恒等于
- 使用变量:$( 变量名 ) 取变量值
- 变量定义及赋值:
obj = a.o b.o c.oobj += d.oCC := gcc - 变量取值:
foo = $(obj)即foo=a.o b.o c.o d.o
-
由 Makefile 维护的一些变量
-
通常格式都是大写
-
CC:默认值 cc
-
-
有些有默认值,有些没有
-
CPPFLAGS: 预处理器需要的选项 如:-I- 例如:
CPPFLAGS=-I/home/jiang/test/include/
- 例如:
-
LDFLAGS: 链接库使用的选项 –L -l- 例如:
LDFLAGS=-L./lib -lmytest
- 例如:
-
CFLAGS: 编译的时候使用的参数 –Wall –g -c
-
-
用户可以修改这些变量的默认值
CC = gcc
-
2.7 自动变量
-
变量(通配符)
- e.g.
main.o:main.c add.c sub.c mul.c dev.c -
[email protected]--> 规则中的目标main.o -
$<--> 规则中的第一个依赖条件main.c -
$^--> 规则中的所有依赖条件main.c add.c sub.c mul.c dev.c - 都是在规则中的命令中使用
- e.g.
-
模式规则(隐含规则)
- 在规则的目标定义中使用 %
- 在规则的依赖条件中使用 %
- %.c %.o 任意的.c 或者 .o
- 可以理解为照葫芦画瓢,需要什么,只要格式能对上,%就变成什么
- 比如终极目标依赖./obj/add.o,那么他就会向下查找,找到
%.o:%.c后,则%=./obj/add,这条规则就变成了./obj/add.o:``./obj/add.c - 再比如:有一条规则
./obj/%.o:./src/%.c,则目标依赖./obj/add.o向下查找依赖时,这条规则就变成了./obj/add.o:./src/add.c,具体应用可参看2.9.3例【3】。
有关*和%的疑惑可以参考:makefile的语法及写法(补充:%和*的区别)
-
示例:
%.o:%.c-
$(CC) –c $< -o [email protected]-
$<--> 表示依次取出依赖条件 -
[email protected]--> 表示依次取出目标值 -
%表示一个或者多个,任意长度的
-
2.8 三个函数
-
makefile中所有的函数必须都有返回值
-
wildcard + 目录/*.后缀-
查找指定目录下指定类型的文件,一个参数
-
src=$(wildcard ./src/*.c)- 找到./src 目录下所有后缀为.c的文件,赋给变量src
-
-
patsubst 格式1, 格式2, 字符串-
匹配替换,将字符串中符合格式1的部分用格式2替换 -
obj = $(patsubst %.c ,%.o ,$(src))- 从src中找到所有.c 结尾的文件,并将其替换为.o
-
obj = $(patsubst ./src/%.c, ./obj/%.o, $(src))- 指定.o 文件存放的路径 ./obj/%.o
-
-
dir=$(notdir $(src))- 去掉src中的路径
-
例子:假设./src中有main.c add.c sub.c mul.c dev.c等
-
src=$(wildcard ./src/*.c)—> src=./src/main.c ./src/add.c ./src/sub.c … -
dir=$(notdir $(src))—> dir=main.c add.c sub.c … -
obj = $(patsubst ./src/%.c, ./obj/%.o ,$(src))—> obj=./obj/main.o ./obj/add.o ./obj/sub.o …
-
2.9 例子
2.9.1 例【1】
- 像如下图中写的makefile【第一版本】并不好,每次只要有一个.c文件被修改时,都会把所有的.c文件重新编译,会浪费额外的时间,假如项目很大,则效率很低。
- 可以通过添加多条规则解决问题(如下图所示【第二版本】),其中第一条规则中的目标为终极目标,而其他规则中的目标为子目标,子目标是为了生成第一条规则中的依赖条件而存在的。
- 执行make,当要生成第一条规则中的终极目标时,会查找其依赖是否存在;如果不存在,则去下面其他规则中的子目标中查找,看是否有相应子目标可以生成不存在的依赖,有则先执行子目标规则的命令;如果存在,则通过对比目标和所需依赖的修改时间,若所需依赖比目标新,则更新目标文件。
- 执行make结果如下:
- 当只修改add.c文件后,执行make结果如下:
- 利用变量、隐藏规则、通配符对【第二版本】makefile中冗余的部分改进,使其更精简,生成【第三版本】如下图
- 利用两个函数将【第三版本】中的obj的写法改进为【第四版本】如下图。如果一个项目很大,含有很多源文件,那么生成可执行文件时,中间就需要生成很多目标文件作为依赖,如果像之前版本中一一列出目标文件,e.g.
obj=main.o a.o b.o c.o ....很麻烦,可以用查找和匹配替换命令轻松实现。
2.9.2 例【2】
-
我的环境说明:
- 其中已将动态库libmytest.so所在绝对路径追加入到/etc/ld.so.conf文件中
- main.c文件中调用位于./lib目录下动态库libmytest.so中的函数add,引用位于./include头文件中的head.h
-
我编写的makefile文件:
2.9.3 例【3】
-
我的环境说明:
-
问题:想要只把src中四个.c文件编译并生成到./obj目录,不生成最终的可执行文件
- 借鉴了别人的方法,使用伪指令的方法实现
-
我编写的makefile文件:
我也是刚刚接触这个东西,自己做了两个小例子,一定不是最优的写法,但是大致把makefile的基本语法用了一遍,而且效率绝对比写命令行要高的。
3. 速览makefile(分五层)
本部分内容为b站某关于makefile的课程笔记,点此查看课程
Makefile是一种脚本语言,Linux C/C++必须使用的一个编译脚本
3.1 第一层:显示规则
-
#是注释 -
目标文件 : 依赖文件[TAB键]指令
说明:第一个目标文件是最终目标,类似递归
例子:
执行:make - 伪目标:.PHONY :
例子:
执行:make clear或make clearall
3.2 第二层:(类似C宏定义)
- 定义变量:
变量=是替换变量+=是追加变量:=是恒等于 - 使用变量:
$( 变量名 ) - 示例:
原始makefile:
使用变量替换后的makefile:
3.3 第三层:隐含规则
%.c %.o 任意的.c 或者 .o
*.c *.o 所有的.c或者.o
示例: 有很多的.o文件待生成 但是其生成规则相同,都是需要gcc -c %.c -o %.o来生成
3.4 第四层:通配符
- 所有的目标文件
[email protected]所有的依赖文件$^所有的依赖文件的第一个文件$<