1,gdb调试
前提:需要对应用程序进行调试,就必须在用gcc编译的时候添加参数-g生成调试信息。
启动gdb调试命令: gdb 包含调试信息的应用程序名字。之后就可以使用gdb的命令进行操作
- gdb命令
1)查看代码
l 查看程序的源代码,默认显示的是包含main的那个文件,下面的命令显示指定的。
l 需要查看的文件的名字:行号 显示指定文件的多少行
l 需要查看的文件的名字:函数的名字 显示指定文件的函数,可能显示不全,按l显示后面的内容,回车是执行前面的命令。
2)断点
break|b 行号 ,在指定行打断点,也可以打条件断点,如下
break|b 行号 if i == 15(指定自己的条件) ,作用是使程序达到条件的时候才往下执行。
info|i break|b, 查看断点信息包括断点编号,输出的信息中有一列Enb,含义标记断点是否有用。
d 断点编号,删除断点。
3)启动程序,调试
start, 开始调试,即windows中IDE下的小虫子按钮,但是只执行一步,使用下面命令继续调试。
n ,单步调试,一步步执行
c ,一直直执行,直到断点结束
s ,进入到函数体内部
u ,跳出当前循环
finsh,跳出当前函数,需要注意的是跳出去之前需要把这个函数中的断点全部删除。
4)查看变量值即变量类型
p 变量名 ,就可以查看变量的值的
ptype 变量名,就可以查看变量的类型
5)追踪变量及取消变量
display 变量名,就可以追踪变量的值,注意取消追踪的时候不是直接undisplay 变量名就行,正确方法见下行。
undisplay,所追踪变量的编号,这样来取消追踪,注意,这里的所追踪变量的编号以命令info|i display获取
6)调试时设置变量值,按自己的意愿调试
set var 变量名=值,可以通过这个命令来设置程序中变量的值,来达到跳过一些没必要了解的程序的执行信息。比如,有一个循 环for(i=0; i<100; i++) 调试到这一步的时候,执行语句 set var i = 50,就可以让程序从i=50开始停止执行。
7)退出gdb模式
quit, 退出gdb模式
2,makefile的编写
makefile是一个项目的代码管理工具,如果项目很大,需要编译的文件很多,那么编译的时候gcc就需要写的很长很长,这个时候就需要使用到makefile了
- makefile的命名规则,Makefile或者makefile,只有这两种方式。
- makefile的编写规则
规则中的三要素:目标,依赖,命令。了解了三要素,下面介绍makefile的编写模式如下:
目标:依赖条件(依赖条件是指需要编译的文件)
Tab(这里的tab缩进是必须的) 命令(命令是指编译的命令)
上面的两行就构成了编写makefile的一条规则。
第一种写法:
app:main.c add.c sub.c
gcc mian.c add.c sub.c -o app
第二种写法:
使用第一种写法存在以下缺陷,如果我们只修改了依赖文件中的一个文件,那么我们需要把所有的文件都重新编译,显然这是一种效率低的做法。使用将依赖拆分的方法就可以解决这个问题。
//这条规则是终极目标
app:main.o add.o sub.o
gcc mian.o add.o sub.o -o app
//下面三条规则是生成依赖
main.o:main.c
gcc -c main.c
add.o:add.c
gcc -c add.c
sub.o:sub.c
gcc -c sub.c
- makefile工作原理
注意,程序执行第一行时,会找到依赖中的main.o,此时的目录下没有这个文件,就会往下找,寻找下面的规则是否可以生成。如果找到,就执行对应的命令来生成所需要的依赖,需要add.o和sub.o时也是如此,当所有的依赖全部找全时,就会执行终极的编译命令(终极目标的编译命令是最后执行的)。注意,这里的.c文件是和makefile放到了一个目录下,不然需要注意路径的问题。
为什么第二种方法就不会产生不必要的编译呢?原因是因为,识别makefile的程序,会根据每条规则的第一行来比较依赖和目标的时间,当目标的时间晚于依赖时,就会执行下面的编译命令,否则跳过。和上面介绍的顺一样,这是从终极规则开始检索的,类似于树的深度搜索。
- makefile中的变量
makefile中的变量是不需要类型的,直接起名字赋值即可。使用变量时,需要用$进行取值,并且需要使用()把变量名括起来。
- makefile中的模式
就像数学公式一样,如果多条规则的格式差不多时,可以写一个模式来进行匹配,而不需要重复写多条规则。
以上面第二种makefile代码为例,最后的三条规则可以写成一条模式如下:
%.o:%.c
gcc -c $< -o $@
这条模式中%的作用是进行模式匹配,在终极规则中遇到XXX.o时,就会向下面的规则检索,此时%.o:%.c就会变成XXX.o:XXX.c,然后就执行命令行
makefile中的自动变量(只能在命令行使用):
$< 规则中(这条规则中的)的第一个依赖
$@ 规则中(这条规则中的)的目标
$^ 规则中(这条规则中的)的所有依赖
经过变量和模式的使用,优化第二种makefile的代码后如下:
obj = main.o add.o sub.o
target = app
$(target):$(obj)
gcc $(obj) -o $(target)
//模式公式
%.o:%.c
gcc $< -o $@
- makefile中的函数
在makefile中所有的函数都是有返回值的。
src=$(wildcard ./%.c) (wildcard是函数的名字,参数直接在空格后面写,后面是目录,利用的是匹配,找到所有目录下的.c文件,函数返回的是一个串,使用$()的形式把数据取出来)
obj=$(patsubst ./%.c,./%.o, $(src))(patsubsts是函数的名字,是一个匹配替换模式,注意函数名和第一个参数之间使用空格隔开,后面的参数使用“,”隔开,这个函数的作用是将src中所有的.c文件都替换成.o文件)
#obj = main.o add.o sub.o
target = app
#makefile中自己维护的变量,下面的CC就是,可以根据用户需要赋值
#makefile中的函数的使用
src=$(wildcard ./*c)
obj=$(patsubst ./%.c, ./%.o, $(src))
CC=gcc
$(target):$(obj)
$(CC) $(obj) -o $(target)
#模式公式
%.o:%.c
$(CC) $< -o $@
#.PHONY是生成为目标,加上之后在执行make clean的时候就不会在和makefile目录下的clean文件比较
.PHONY:clean
clean:
-mkdir ./aa #前面加-的目的是执行失败之后,跳过这条命令
rm $(obj) $(target) -f
hello:
echo "hello world!"
注意,最后两条条规则,没有依赖,倒数第二条的作用是删除.o文件和生成的应用程序。使用的时候,直接make clean即可调用。同样,执行make hello 就可以输出hello world!
需要注意的是,如果不进行为目标设置,如果在makefile目录下,如果有一个名为clean的文件,那个make clean就不能被正常执行,因为前面说过makefile有自己的更新模式,实际上规则中的clean是没有生成的,所有目录中的clean永远是最新的,所以下面的rm命令永远不会执行。因此需要使用.PHONY来声明伪目标,这样在执行make clean命令时,就不会比较,然后直接执行rm命令。
makefile写成上面的样子基本就可以了。