Linux相关概念和重要知识点(6)(make、makefile、gdb)

news/2024/9/25 18:21:25/

1.make、makefile

(1)什么是make、makefile?

在我们写完代码后,要编译运行,如果有多个.c文件就需要每次都自己用gcc -o来处理,这十分麻烦。当我们想要自定义多个文件的处理时,我们会浪费很多时间在重复的事情上,我们需要一种自动化的、可自定义文件处理方式的程序,make和makefile就是为此而生的。大型项目一定离不开make和makefile。

make和makefile联系紧密,make是一个命令,makefile是一个文件,这个文件以某种格式来自定义可执行程序的方式,当使用make时就调用这个makefile文件来处理。

(2)makefile结构

makefile本质是依赖关系和依赖方法的集合。举个例子,完成老师向学生下达任务这件事需要两个条件:首先就是这两个人得是师生关系,即依赖关系;其次就是下达任务这个动作,这叫依赖方法。要办成一件事,依赖方法和依赖关系缺一不可,首先建立依赖关系,其次传递依赖方法。

格式:(目标文件):(依赖文件)   -> 依赖关系

(tab) (依赖方法)

类似于函数的传参和返回值,依赖文件类似传递的参数,而目标文件则是返回值,或者说是计划生成的文件。隔行后就可以开始写我们的依赖方法了,类似函数体。但是要注意每行依赖方法指令必须要顶格使用tab,就像python的函数一样,对缩进的要求很高。

下面是一个简单的示例。

当我们执行make时,就会根据makefile在当前目录下找test.c文件,类似函数的参数,目标生成的文件就是test。在依赖方法中我们显式写了目标文件和依赖文件,名字也符合规范。按照我们的指令一行行执行,最终生成指定文件。

我们发现,我们不仅将指令的结果打印出来了,还将指令的内容打印出来了。当我们在依赖方法每行前面加上@时,就能只打印结果而没有指令本身,这叫关闭回显。

(3)依赖关系

我们需要进一步理解依赖文件和目标文件的作用,根据上面的例子,有人肯定会觉得目标文件和依赖文件就等同于函数的参数和返回值,但事实并不是,它们和函数有很大区别

看一下下面的依赖关系和结果

我们发现,目标文件是a,没有依赖文件,但是实际执行指令时用到了test.c,生成的文件又是test,和目标文件a完全不沾边,这应当如何解释?

目标文件理解成一种建议生成的文件或是一种规范,规范更好,不规范也不会强制终止。因为在有的时候不需要生成文件,而目标文件一定要写,所以这是一种妥协。但是我们必须保证依赖文件必须能够找到,因为不需要依赖文件时我们可以不写,不会存在目标文件的那种情况。当执行依赖方法时,每一行指令其实都被展开到当前工作目录下执行,上面的gcc -o test test.c是一句完整的指令,它不需要依赖任何的依赖文件,也能合理地生成自己的文件,和目标文件也没有任何关系。

那么依赖文件和目标文件存在有何意义?

①声明依赖关系,实现不同功能的区分

在makefile我们可以写很多指令,实现不同功能,不同功能的指令都需要先声明依赖关系,再执行依赖方法。依赖文件和目标文件存在就是为了声明依赖关系。在目标文件和依赖文件没有实际意义时它更像一种标识,标志这个功能将要开始实现,也可以和其它功能的指令作出区分。

当我们使用make时默认执行第一个依赖关系下的依赖方法。要执行其它的需要make (目标文件)

在makefile里,目标文件名不能重复,否则会导致歧义。

②自动推导

在很多情况下依赖文件和目标文件是一种建议,是一种规范,规范必定带来好处,其中有一个就是自动推导。

当我们想要通过.c先生成一个.o,再生成一个可执行文件,那就可以利用自动推导来处理。

当make之后会自动执行第一个依赖关系,按照我们写的依赖文件在目录中查找,如果找不到,会将这个依赖关系先放入栈,再找目标文件是刚刚依赖文件的依赖关系,找到了如果发现依赖文件还是找不到,那就继续入栈,以此类推。如果一个依赖关系的目标文件和依赖文件都能在当前目录找到,那就依次出栈,执行指令。

注意推导到最后的依赖关系时依赖文件一定找得到,否则就报错,和前面的规则一样。

根据上面的规则,看一下下面的makefile,在make和make clean时结果是什么

结果

当make时,会根据目标文件和依赖文件入栈出栈,当我们递归生成文件时,就需要好好规范一下目标文件和依赖文件的写法,保证能够推导到其它依赖关系上。

③通配符

当我们不写依赖文件时,依赖方法里依然可以用到目录里面的文件,因为依赖方法的指令会被展开执行,既然显式写出了调用的文件(即构成了一句完整的指令),就能正常执行。

当我们有多个.c时,我们需要逐个对所有.c进行编译,当要处理的文件都有一个相同特征时,通配符就派上用场了,在makefile里,百分号%就是通配符的意思,和指令里的*一样

注意通配符对应的依赖关系很特殊,要么整个依赖关系都用通配符,要么都不用。并且make时是无法定位由通配符组成的通配符的依赖关系的,就算名义上它是第一个依赖关系。需要我们写一个自动推导的依赖关系。

④变量和自动变量

如果我们想要依赖关系列出来的文件和实际依赖方法使用的文件有较强相关性,可以使用自动变量。

$@代表目标文件,$^代表全部依赖文件,$<代表逐个依赖文件

定义的变量可以以$(变量)形式使用,变量和自动变量的使用可以帮助我们更统一的管理文件调用,生成的文件,极大降低了重复的工作

(4).PHONY伪目标

常规的依赖关系都会根据依赖文件和目标文件的Modify来决定是否需要执行。如果依赖关系里面的文件都有且目标文件的Modify时间更晚,那就不会执行。

.PHONY:(伪目标)声明伪目标,会自动忽视伪目标的时间比对。后续直接使用(伪目标):(依赖文件)来定义依赖关系即可。我们也可以用touch显式修改时间来绕过检查。

一个.PHONY声明一个伪目标

伪目标的作用就是为了执行一些指令,不要求生成文件生成

2.gdb

gdb是调试代码的工具,我们利用gdb调试的本质是帮助我们快速找出问题,然后自己解决。这里分享一些指令和注意事项

cgdb:我们可用cgdb来替代gdb,操作一致但代码有展示,更方便。

检查是否安装:gdb --version

调试前提:编译选项加上-g(debug,添加调试信息)。默认情况都是release,无调试信息

重复操作:gdb中直接回车默认执行上一条指令

显示代码:l (num)将第num行居中显示,l (函数名)居中显示当前函数名所在行,l (源文件):(函数名/行号)查看指定文件中的代码,注意每次回车都会执行l(翻页,每次10行)

打断点:b (行数/函数名)给指定位置打断点,info b可以查看自己打的断点,每个断点都有一个编号Num

打条件断点:b (行数) if (变量) == (值)打一个条件断点,只在if成立时截停。condition (断点编号) (条件) 可以给已有的断点添加条件

暂时禁用断点:enable (断点编号)打开断点,disable (断点编号)关闭断点

删除断点:d (断点编号)删除断点,要通过info b查看自己打的断点的编号Num。注意每个断点编号对应一次断点操作,不会复用已删除的断点编号

执行代码:r在没有断点时直接将代码运行完,有断点时执行至第一个断点。调试过程中再按r意味着重新启动调试

逐语句和逐过程:n逐过程(不会进入函数),s逐语句(会进入函数)

执行至下一次断点:c直接执行到当前断点的下一个断点

执行至指定行数:until (行数)跳过中间的代码,直接执行到指定行

按函数运行:finish能把当前函数跑完,如果期间有断点会被截停,main函数不行(最外围函数不支持)。断点 + finish + until + c这几个指令组合起来很有用,能帮助我们查找bug发生点,进而实现对大的代码块进行区间debug

调试中修改:set bar (变量)=(新值),直接修改代码中的变量的值,帮助我们在调试时能根据猜想及时检验问题,如果错了也可以继续修改,而不必退出gdb检验

监视:display (变量)每次执行都会监视变量,display (监视编号)关闭显示,p (表达式)临时显示当前变量或表达式的值。监视编号也是不可复用的,每次打印监视变量就会打印其监视编号

检测变量:watch (变量)检测变量的值是否变化,它是watchpoint,能用info b查看,也能d删除。如果变量数值变了,就会显示出来,而不像display那样每次都显示

查看当前函数栈帧:bt显示逐语句位置,显示每层函数调用位置

自动变量:info locals显示当前栈帧定义的变量信息


http://www.ppmy.cn/news/1530402.html

相关文章

Pytorchz学习---基于卷积神经网络的MINIST数据集训练

# 一般情况下&#xff0c;如果模型有可学习的参数&#xff0c;最好用nn.Module&#xff0c;其他情况用nn.function相对简单一些 import torch.nn.functional as F import torch loss_func F.cross_entropy def model(xb):return xb.mm(weights)bias# bs 64 # xb x_trains[0:…

微服务注册中⼼1

1. 微服务的注册中⼼ 注册中⼼可以说是微服务架构中的”通讯录“ &#xff0c;它记录了服务和服务地址的映射关系。在分布式架构中&#xff0c; 服务会注册到这⾥&#xff0c;当服务需要调⽤其它服务时&#xff0c;就这⾥找到服务的地址&#xff0c;进⾏调⽤。 1.1 注册中⼼的…

大健康裂变分销小程序开发

大健康裂变分销小程序的开发是一个涉及技术、市场策略、用户体验和合规性等多个方面的综合项目。这类小程序旨在通过分销机制促进大健康产品的销售和品牌推广&#xff0c;同时利用社交网络的裂变效应扩大市场影响力。以下是大健康裂变分销小程序开发的主要步骤和考虑因素&#…

设计模式-PIMPL 模式

PIMPL&#xff08;Pointer to IMPLementation&#xff09;&#xff0c;又称Opaque Pointer模式或编译防火墙&#xff0c;是一种在C中广泛应用的编程技术。其核心思想是将类的实现细节从公共接口中分离出来&#xff0c;通过指向实现类的指针来访问类的具体功能。这种模式在提高代…

C#|.net core 基础 - 深拷贝的五大类N种实现方式

在实际应用中经常会有这样的需求&#xff1a;获取一个与原对象数据相同但是独立于原对象的精准副本&#xff0c;简单来说就是克隆一份&#xff0c;拷贝一份&#xff0c;复制一份和原对象一样的对象&#xff0c;但是两者各种修改不能互相影响。这一行为也叫深克隆&#xff0c;深…

把命令的语气改成聊天的方式

​在与人沟通时&#xff0c;没人会喜欢别人用下命令的口气对自己说话。 即使是非常明显的错误&#xff0c;一旦用粗鲁的口气命令指责别人&#xff0c;也会引起对方的反感&#xff0c;改正起来也会非常困难。 即使作为领导&#xff0c;也不要用命令的语气面对下级要做到合情合…

Python提供内置正则表达式库

正则表达式是一种强大的文本处理工具&#xff0c;可以匹配文本片段的模式 最简单的正则表达式就是普通的字符串&#xff0c;可以匹配自身 要注意的是&#xff0c;正则表达式并不是一个程序&#xff0c;它使用一种特定的语法模式来描述在搜索文本时要匹配的一个或多个字符串。正…

2024最新!!!iOS高级面试题,全!(一)

TCP,HTTP,HTTPS&#xff0c;,WebSokect 区别&#xff1a; IP协议&#xff08;网络层协议&#xff09; TCP&#xff1a;传输控制协议&#xff0c;主要解决数据如何在网络中传输&#xff0c;面向连接&#xff0c;可靠。&#xff08;传输层协议&#xff09; UDP&#xff1a;用户数…