Ⅳ.项目自动化构建工具Make/Makefile
1.Make/Makefile的介绍
makefifile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编 译,极大的提高了软件开发的效率。
make是一个命令工具,是一个解释makefifile中指令的命令工具,一般来说,大多数的IDE都有这个命 令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefifile都成为了一 种在工程方面的编译方法。
make是一条命令,makefifile是一个文件,两个搭配使用,完成项目自动化构建。
2.Make/Makefile的简单使用
(1)Make/Makefile的初始化
首先,我们需要创建一个Makefile文件,命名为Makefile。Makefile的基本格式如下:
1. target: dependencies 2. command
其中,target表示目标文件,dependencies表示依赖文件,command表示执行的命令(依赖方法)。
对于我们的例子,我们可以创建一个Makefile文件,内容如下:
main: main.c gcc -o main main.c
这个Makefile文件表示,我们的目标文件是main,依赖文件是main.c,执行的命令是gcc -o main main.c
接下来,我们在终端中进入程序所在的目录,执行make命令:
make
这时候,Make会自动读取Makefile文件,根据文件中的规则进行编译和构建。如果一切顺利,我们就可以在当前目录下看到一个名为main的可执行文件。
如果我们修改了main.c文件,需要重新编译程序,只需要再次执行make命令即可。
举个栗子:我们写一个测试,用main.c ,pp.h, pp.cpp三个文件实现
编写 makefile,最重要的是理解依赖关系和依赖方法:
- 依赖关系—— 目标文件:依赖文件
- 依赖方法—— 源文件形成目标文件需要执行的指令
- 被.PHONY修饰的对象就是一个伪目标
(伪目标 不依赖任何文件,依赖方法是 rm -f 指令;其中 .PHONY 修饰 clean 表示其是一个伪目标,伪目标总是被执行)
建立一个Makefile文件,注意文件名必须是makefile或Makefile,不能是其他名称,否则make不能识别。
注意:
依赖方法是 gcc 编译,依赖方法的执行指令必须以 [Tab] 键开头,特别注意不能是四个空格
例:[TAB]+g++ pp.cpp -o pp.o
(2)Make/Makefile的使用
接下来,我们在终端中进入程序所在的目录,执行make命令:
删除make指令生成的文件则用刚刚定义的clean
make clean
伪目标总是被执行 的意思就是不管你有没有文件或修改,都会执行定义的clean指令
相对应的就是make指令:只有没有文件或文件被修改才能使用
补充:
可通过 格式:stat+文件名 查看文件的属性信息
Access 指最后一次读取的时间(访问)(比如在终端上用cat、more 、less、grep、 cp 、file 一个文件时都应该更新这个时间,不过它不会每次都更新,因为访问是个非常频繁的操作,每一次我们有意无意的操作,系统都更新它的时间是没必要的)
Modify 指最后一次修改数据的时间(修改)(意思为更改,更改的是内容,“或者“写入)
Change 指最后一次修改元数据的时间(改变)(意思为改变,改变的是状态或属性,比如对一个文件或者目录作mv、chown、chgrp操作)
Ⅴ.gdb调试代码
1.什么是gdb
GDB (GNU Debugger)是一个强大的命令行工具,可以帮助开发人员调试用各种编程语言(包括 C、 C + + 和汇编)编写的程序。它适用于大多数基于 Unix 的系统,包括 Linux 和 macOS。
2.debug和release
(1)Debug称为调试版本,它在源代码的基础上不作任何优化,便于程序员调试程序。
(2)Release称为发布版本,它往往是对程序进行了各种优化,例如减小程序大小和加快运行速度等,以便用户很好地使用。
(3)这两种状态下的代码运行结果可能会不同。
只有debug版本的程序才能调试,gcc默认生成的程序是release版本的,如果想生成debug版本的可执行程序可以在编译语句后面加上-g
若用release版本调试则
需要输入:
g++ fib.cpp -o fib -g
生成debug版本才能使用
3.gdb调试操作
以调试fib为例,简单说明最基本的几个命令,下面是gdb的操作方法。源码如下:
#include<iostream> #include<queue> #include<vector> using namespace std; int f[100]; int n; void print(int n) { for(int i=1;i<=n;i++) cout<<f[i]<<" "; } int main() { cout<<"hello"<<endl; cout<<"I"<<endl; cout<<"am"<<endl; cout<<"linux"<<endl; f[1]=1; f[2]=1; cin>>n; for(int i=3;i<=35;i++) { f[i]=f[i-1]+f[i-2]; } print(n); cout<<n<<endl; return 0; }
(1)进入调试
格式:gdb 可执行程序文件
输入:gdb fib 进入调试,进入gdb就可以输入指令了
(2)显示源代码
格式:list/l 数字
list 默认展开源代码的前十行,如果还想看到后面的内容可以点击Enter十行十行展开,加入数字表示从第几行显示
(3)断点的添加、删除和信息显示
指令 | 全称 | 功能 |
b | breakpoints | 打一个断点 |
info b | info break | 查看当前断点 |
d | delete breakpoints (n) | 删除断点 |
格式:break+代码行数/b+代码行数 || break+代码行数+if+条件/b+代码行数+if+条件
格式:info break/info b
格式:delete+断点编号/del+断点编号
如上图的演示,打断点的时候,b后面加行号,删除断点的时候d后加断点的序号
(4)run运行调试程序与continue继续运行
指令 | 全称 | 功能 |
r | run | 调试运行 |
n | next | 逐过程调试 |
s | step | 逐语句调试 |
c | continue | 运行到下一个断点处停止 |
格式:run/r r 开始运行到断点
格式:continue/c c 继续运行至断点或者程序结束
格式:next/n next为gdb的逐语句调试,当遇到函数调用时,不进入函数体,相当于VS的F10
格式:step/s step为gdb的逐过程调试,当遇到函数调用时,进入函数体,相当于VS的F11
(6)查看某个变量的值
指令 | 全称 | 功能 |
p | 临时查看变量值 | |
display | 常显示变量 | |
undisplay | 取消变量常显示 |
格式:print var(var表示任何一个变量或表达式)
在调试过程中,我们需要查看当前某个变量值的时候,可使用 print 命令显示该值,但是只能显示一次下次不会再显示。
格式:display var(var表示任何一个变量或表达式)
使用 display 命令就可以一直显示该值。
undisplay只能删除所有显示值
(7)退出gdb
VS中退出调试可以输入Shift+F5
格式:quit/q
4.其他命令
指令 | 全称 | 功能 |
bt | breaktrace | 查看函数堆栈 |
finish | 执行完当前函数 | |
until+n | 跳转到指定行 |
until:当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体
until+行号: 运行至某行,不仅仅用来跳出循环
finish: 运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息
call 函数(参数):调用程序中可见的函数,并传递“参数”,如:call gdb_test(55)
disable 断点号n:暂停第n个断点
enable 断点号n:开启第n个断点
clear 行号n:清除第n行的断点