GDB
(1)下载安装gdb: sudo apt-get install gdb
(2)启动gdb gdb test
(3)启动后界面如下:
参考老师所给ppt内容
我们可以输入(gdb) l列出文件的代码清单
·(gdb) b
1.函数断点:在进入指定函数时停住。
2.行断点:在指定行号停住。eg: (gdb) b 8
3.条件断点:break ... if
4.临时断点:tb 行号
**·(gdb) r **运行被调试的程序。如果此前没有下过断点,则执行完整个程序;如果有断点,则程序暂停在第一个可用断点处。
**·(gdb) p i **显示指定变量(临时变量或全局变量)的值 ,这里就是打印输出变量i的值
·(gdb) finish 使一个函数结束
**·(gdb) q **退出断点调试
Makefile
什么是makefile?
makefile关系到了整个工程的编译规则。一个工程中的源文件不计其数,并且按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个 Shell脚本一样,其中也可以执行操作系统的命令。
makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
compile和link(链接)的概念
源码compile(检查语法,有函数声明即可通过)后得到中间文件(UNIX是.o);
对中间文件进行打包得到库文件(UNIX是.a);
把它们link(将xx.o和xx.a合成一坨一个可执行文件)——链接函数和全局变量等,找不到函数实现就会出现link error——得到可执行文件(UNIX无后缀)
makefile和shell的区别
作用:
·shell是为了重用terminal内的命令服务的,是系统程序员与OS内核的交互接口;
·makefile是用于工程compile/link用的;
·makefile内可写shell命令、调用shell脚本,如:+ ./xxx.sh;
·shell内 =号不允许有空格,makefile内 =号空格无碍;
·shell内开头要写 #!/bin/bash,后缀名是.sh;
·shell内是自上而下执行,有数组、循环等;makefile内执行shell命令是一行独立一个进程,若想一个进程统一执行,得在末尾用 \ 表同一行。
·shell内通配符:*;makefile内通配符:%;
·shell内变量用 {},命令串用 ();makefile中访问变量用 $() 或 ${} 都行;
编写格式:
目标target: 所需prerequisites
(TAB) command (任意的Shell命令)
label:
(TAB) command...
makefile简单示例
示例①:我们创建一个project文件夹,在里边新建一个简单的test.c工程:
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
根据我们上文列出的makefile编写规则
target: dependencies****
command
我们编写以下makefile文件
先用vim makefile
创建一个makefile文件,文件内写入如下内容:
test: test.c
gcc test.c -o test
发现运行成功
事例②:
我们创建以下几个文件:
main.c:
#include "tool.h"
#include "bar.h"
#include <stdio.h>
int main()
{
int arr[5] = {1,9,3,8,0};
int min = find_min(arr, 5);
int max = find_max(arr, 5);
printf("min = %d\n", min);
printf("max = %d\n", max);
return 0;
}
tool.h:
int find_max(int arr[], int n);
tool.c:
#include "tool.h"
int find_max(int arr[], int n)
{
int m = arr[0];
for(int i = 0; i < n; i++)
{
if(arr[i] > m)
{
m = arr[i];
}
}
return m;
}
bar.h:
int find_min(int arr[], int n);
bar.c:
#include "bar.h"
int find_min(int arr[], int n)
{
int m = arr[0];
for(int i = 0; i < n; i++)
{
if(arr[i] < m)
{
m = arr[i];
}
}
return m;
}
第一阶段makefile
编写makefile:
main: main.c tool.o bar.o
gcc main.c tool.o bar.o -o main
tool.o: tool.c
gcc -c tool.c
bar.o: bar.c
gcc -c bar.c
clean:
rm *.o main
首先,要从最终生成的可执行文件写起,此处为 main。还是先写
target - main,dependencies - main.c tool.o bar.o,
换行之后按下 tab 键,再写 command - gcc main.c tool.o bar.o -o main
。
然后,开始写依赖项 tool.o
和 bar.o
。
依赖中间文件 .o 文件生成最终的可执行文件,可以避免编译整个文件。哪个文件发生了改变,只需编译该文件对应的 .o 文件即可。之后再与其他文件共同生成最终的可执行文件。
从图中可以看出,terminal 中的执行顺序是与 Makefile 中的书写顺序相反的,将 Makefile 中的内容由下至上执行
第二、三阶段makefile
makefile变量
使用OBJ变量、省去指令内容,自动推导:
这种自动推导过程叫做隐含规则
“隐含规则”也就是一种惯例,make会按照这种“惯例”心照不喧地来运行,那怕我们的Makefile中没有书写这样的规则。例如,把[.c]文件编译成[.o]文件这一规则,你根本就不用写出来,make会自动推导出这种规则,并生成我们需要的[.o]文件。
第四阶段makefile
自动变量($^ $< $@)
具体意义如下:
$@ :指代当前规则下的目标文件列表
$< :指代依赖文件列表中的第一个依赖文件
$^ : 指代依赖文件列表中所有依赖文件
$? : 指代依赖文件列表中新于对应目标文件的文件列表
多个文件makefile的编译
如果有多个文件,这时候不能并列写编译,而是要在开头添加all声明
依旧拿我们刚才的例子,这次我们在两个目标程序前添加一个all,随后编译运行,成功~
makefile:
all:main_max main_min
main_max: main_max.c tool.o bar.o
gcc main_max.c tool.o bar.o -o main_max
main_min: main_min.c tool.o bar.o
gcc main_min.c tool.o bar.o -o main_min
tool.o: tool.c
gcc -c tool.c
bar.o: bar.c
gcc -c bar.c
clean:
rm *.o main_max main_min
makefile的工作原理
目标的生成: a. 检查规则中的依赖文件是否存在; b. 若依赖文件不存在,则寻找是否有规则用来生成该依赖文件
比如上图中,生成calculator的规则是gcc main.o add.o sub.o mul.o div.o -o,Makefil会先检查main.o, add.o, sub.o, mul.o, div.o是否存在,如果不存在,就会再寻找是否有规则可以生成该依赖文件。
比如缺少了main.o这个依赖,Makefile就会在下面寻找是否有规则生成main.o。当它发现gcc main.c -o main.o这条规则可以生成main.o时,它就利用此规则生成main.o,然后再生成终极目标calculator。
目标的更新: a. 检查目标的所有依赖,任何一个依赖有更新时,就重新生成目标; b. 目标文件比依赖文件时间晚,则需要更新。
参考博客:
[https://blog.csdn.net/weixin_44498318/article/details/115769976?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-8-115769976-blog-125890413.pc_relevant_multi_platform_whitelistv3&spm=1001.2101.3001.4242.5&utm_relevant_index=11](Linux Makefile快速入门)
https://blog.csdn.net/qq_34623621/article/details/121537317