Makefile与gdb调试

gdb

不检查语法错误,在使用gdb之前要生成可调式的执行文件

1
2
gcc -g xx -o xx
gdb xxx

list (l)看代码

b break 行号断点 然后 run 直接到断点没执行 info b 查看断点表

条件断点 b 20 if i = 5 ,当 某个变量满足条件才触发断点

n next 走 s step 进入函数

p print 变量 看变量的值

until n 跳到n行能快速执行完中间的代码

continue 继续执行断点后续指令 也就是直接跳到下一个断点

finish执行完当前的函数。

quit 退出gdb

如果段错误 gdb 上来直接run 既可以知道哪行

start 直接从第一行开始执行,finish 结束当前函数调用返回函数调用点

set args 设置main的命令行参数 / run 参数1 参数2

ptype 查看变量类型

backtrace bt, 显示当前栈里的栈帧 frame 切换函数的栈帧

假设已经进入 a() 但是想要输出main栈帧的p变量 显然不行 所以要切换栈帧

1
2
3
4
5
6
7
8
void a(){
    for(int i = 0;i<100;i++) printf("%d",i);
}

int main(){
    char* p = "aaa";
    a();
}

display 绑定显示变量 如果在循环里不想一直 p 变量

undisplay 编号 卸载绑定显示

makefile

把一些编译命令放到一个文件里 意义。

命名只能makefile 除非-f指定

1个规则

目标:依赖条件 一个 tab 缩进命令

1
2
生成的文件名:源.c(依赖项)
	gcc 源.c -o 生成的文件名字

如果找不到依赖项就会往下面的规则寻找来生成依赖 ,所以是自上而下的顺序 第一个既为最终生成的程序,当然可以指定最终生成的程序 ALL: 指定最终,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
d.out : a.o b.o c.o d.o
	g++  a.o b.o c.o d.o -o d.out 
	
a.o : a.cpp
	g++ -c a.cpp -o a.o
b.o : b.cpp
	g++ -c b.cpp -o b.o
c.o : c.cpp
	g++ -c c.cpp -o c.o
d.o : d.cpp
	g++ -c d.cpp -o d.o

虽然可以通过一个-o一个指令来完成 makefile的意义在于可以不用重复编译。当有一个文件频繁修改 如果直接一条指令编译 那么其他所有的文件 即便没修改也要重新编译, makefile 通过生成.o的文件 编译到链接部分 再加上ctime修改访问时间的查看

发现 你生成的.o 的时间 比 依赖项的时间还要早 显然依赖项 也就是源文件是被修改过的 所以这个文件就会重新编译

2个函数

只有一种类型 就是字符类型 其中${}为函数调用$()为取变量, 第一个参数为函数名 从后就是形参列表

*%区别就是 *是可变绑定,而 %是锁定 第一个%匹配后 第二个次调用还是一样

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 把*当前目录*所有以.c结尾的文件名字 赋给src 
src = ${wilecard *.c} #src = a.c b.c c.c ...
src = ${wilecard ./src/*.c} #src = ./src/a.c ./src/b.c ...
#将参数3中包含参数1的部分替换为参数2
obj = ${patsubst %.c , %.o , $(src)} #obj = a.o b.o c.o
obj = ${patsubst src/%.c , ./obj/%.o , $(src)} #obj = a.o b.o c.o

d.out : $(src)
	g++  $(obj) d.out 
	
a.o : a.cpp
	g++ -c a.cpp -o a.o
b.o : b.cpp
	g++ -c b.cpp -o b.o
c.o : c.cpp
	g++ -c c.cpp -o c.o
d.o : d.cpp
	g++ -c d.cpp -o d.o

clean :

-rm -rf $(obj) a.out - 的作用去除报错 即便文件不存在也删除

1
make clean -n #预先演练一遍 防止删错 去掉n真正执行

make后面的参数会传入 如果设置了wildcard就会被捕捉

3个自动变量

$@ 规则的命令中表示目标

$^ 在规则的命令中表示所有的依赖条件

$< 规则的命令中表示第一个依赖条件 如果将该变量应用在模式规则中它可将依赖条件列表中的依赖依次取出,套用模式规则

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
src = ${wilecard *.c} #src = a.c b.c c.c ...
obj = ${patsubst %.c , %.o , $(src)} #obj = a.o b.o c.o



d.out : $(src)
	g++  $(obj) d.out 
	
a.o : a.cpp
	g++ -c $^ -o $@
b.o : b.cpp
	g++ -c $^ -o $@
c.o : c.cpp
	g++ -c $^ -o $@
d.o : d.cpp
	g++ -c $^ -o $@

模式规则

1
2
3
4
5
6
7
src = ${wilecard *.c} #src = a.c b.c c.c ...
obj = ${patsubst %.c , %.o , $(src)} #obj = a.o b.o c.o

d.out : $(src)
	g++  $(obj) d.out 
%.o : %.c
	gcc -c $< -o $@ #$^ also

静态模式规则

1
2
3
4
5
6
7
#消除如下歧义
%.o:%.c
	gcc ...
%.o:%.c
	gcc ... #不知道选谁
#把前面的写
$(obj):%.o:%.c #此时就会选静态

伪目标

如果存在cleanALL的文件 执行make clean 就失效 因为clean修改时间没变 用伪目标,不管条件是否满足都执行

1
.PHONY : clean ALL

* 是用来匹配 而%是用来占位符

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
src = ${wildcard ./src/*.cpp}
obj = ${patsubst ./src/%.cpp, ./obj/%.o, $(src)}
Args = -Wall -g
HeadPath = ./inc
ALL:d.out

d.out:$(obj)
	g++ $(obj) -o d.out 

./obj/%.o:./src/%.c
	g++ -c $^ -o $@ -I ./inc
-PHONY: clean ALL 

最终版

作业: 对当前目录 多个源码程序进行编译 该源码不依赖于其他自定义头文件 库文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
src = ${wildcard *.cpp}
out = ${patsubst %.cpp,%,$(src)}
ALL:$(out)


% : %.cpp # % 匹配的是ALL的规则, ALL依赖的out
	g++ $< -o $@
clean:
	-rm $(out)
-PHONY:clean ALL