【Linux】--make/makefile--gcc/g++/gdb

make/makefile

概念

make是Linux下的一条指令,而Makefile是一个文件,可以简单理解为make就是执行Makefile里面写的目的。

会不会去写Makefile可以侧面的看出一个人是否具备完成大型工程的能力

假设现有一个工程里面有上千上万个源文件,那么就可以使用Makefile定义一系列的规则来指定,哪些文件需要先编译,哪些文件后编译。当我们写好Makefile后,只需要一个make命令就可以让整个工程完全自动编译,极大地提高了效率

也就是说,make是一个解释Makefile中指令的命令工具,两者搭配使用就可以完成项目自动化构建

Makefile

那么Makefile应该怎么样去写呢,那就得先了解一下Makefile的原理了。

在Makefile里面我们需要去定义一个方法时,就得考虑到两个因素,依赖关系依赖方法。举例:当我们编写好一个源文件时,我们需要对它进行编译生成可执行文件,那么可以把这个可执行文件看做是目标文件,想要生成它就得依赖我们写好的源文件,那么我们就可以把源文件看做是依赖文件。那有了源文件后,我们需要通过编译工具编译的方法才能生成可执行文件,那这个方法我们就可以看做是依赖方法。
在这里插入图片描述

首先我写好了一个.c源文件,并且创建了一个makefile文件,按照之前学的,这是我们可以用gcc直接去编译生成可执行文件,但是如果每一次都去写gcc的指令就会很麻烦,因此我们可以考虑将这条指令放进makefile里面,我们只需要执行一下make指令就可以自动调用了。

image-20221029150244348

编写的格式就跟上面所讲,需要有依赖关系、依赖方法。目标文件的后面必须要有跟上一个冒号,并且依赖方法另起一行必须以Tab键开头,不能是几个空格

当我们写好之后,make一下看看效果

image-20221029150538443

可以看到当我们make一下后,系统会自动执行在makefile里面的指令,生成一个可执行文件。但是现在当我们再make一下,还会再次执行吗?

image-20221029150712364

报错了,这是为什么呢?

make指令这点就带来了好处,当我们的源文件没有被修改时并且可执行文件已经生成时,make就会禁止你的再一次编译,那么问题来了make怎么知道源文件有没有被修改呢,这就得说到文件的属性了。

image-20221029151230977

当我们去查看文件的属性时会发现,文件都会有三个时间属性,那么这三个分别代表什么呢

  1. Access:文件最近一次被访问的时间
  2. Modify:文件最后一次被修改的时间
  3. Change:文件的状态被修改的时间

当我们的源文件没有被修改时并且可执行文件已经生成时,此时我们在make一下,make会去比较源文件和已有的可执行文件的被修改时间,如果可执行文件的比源文件的要晚,那么make不再执行编译指令,反之同理

那如果我就想让它执行呢,这时候就引入了一个makefile中的一个关键字:.PHONY(伪目标,该目标总是被执行)

image-20221029151900911

image-20221029151918638

可以看到此时,该条指令就可以无限执行了。

make

make是如何工作的呢

  1. make首先会在当前目录下找“makefile”或“Makefile”的文件
  2. 找到之后,它会找文件中的第一个目标文件,并把该依赖方法执行生成最终的目标文件
  3. 假设现在目标文件为hello,依赖文件为hello.o,但是此时目录下没有hello.o文件,那么make就会继续往下找以hello.o为目标文件的依赖关系,以此类推。
  4. 也就是说make会一层一层的去找文件的依赖关系,直到最终编译出第一个目标文件
  5. 如果最后被依赖的文件找不到,那么make会直接退出并报错

gcc/g++/gdb

gcc/g++

众所周知,当我们使用C/C++语言写好代码后,我们的先让源文件经过预处理,编译,汇编,链接几个步骤后才会生成一个二进制机器语言,这样计算机才能看得懂并且执行。那么在Windows下我们可以利用集成开发软件去帮我们完成这几个步骤,同理在Linux下我们就需要借用gcc/g++工具来帮我们完成。

预处理

预处理的主要功能包括宏定义,文件包含,条件编译,去注释等操作

我们在编写代码时包含的头文件就会在这个步骤进行展开,宏定义会进行替换,我们写的注释也会在这个步骤去除

在Linux中该步骤生成的文件一般以.i为后缀,我们可以通过命令去完成这个步骤,需要用到-E选项

gcc -E main.c -o main.i

编译

这个步骤,首先gcc/g++会检查代码是否有错误,以及确定代码的实际要做的工作,如果一切正常无误后,gcc/g++就会把代码翻译成汇编语言

在Linux中该步骤生成的文件一般以.s为后缀,我们可以通过命令去完成这个步骤,需要用到-S选项

gcc -S main.c -o main.s

汇编

汇编阶段就是把生成的.s文件转为目标文件也就是生成机器可以识别的二进制语言

在Linux中该步骤生成的文件一般以.o为后缀,我们可以通过命令去完成这个步骤,需要用到-c选项

gcc -c main.c -o main.o

链接

这个阶段,目标文件会和系统库进行连接生成一个可执行程序

Linux中库的命名:去掉前缀lib,去掉后缀.so,剩下的就是库的名称

静态链接

这个链接方式下,代码会将静态链接库拷贝一份到最终的可执行程序中,这样虽然提高了效率但是会是可执行程序的空间变得很大,不利于大型项目

在linux下想要生成静态链接就要在后面加上 -static选项,并且要确保已经安装了C/C++的静态库

sudo yum install glibc-static//安装C静态库
sudo yum install libstdc++-static//安装c++静态库

image-20221029155826995

image-20221029155930787

动态链接

这个链接方式下,在可执行程序执行的时候,会将动态链接库映射到一块虚拟空间中,然后根据需要再去找对应的函数代码,这样可以极大地缩小可执行程序的空间。

Linux下默认的链接方式是动态链接,依赖的动态库一般为.so为后缀

image-20221029155056579

image-20221029155131557

对比上面的静态链接明显大小小很多

选项操作

  1. -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
  2. -S 编译到汇编语言不进行汇编和链接
  3. -c 编译到目标代码
  4. -o 重命名可执行文件
  5. -static 此选项对生成的文件采用静态链接
  6. -g 生成调试信息。GNU 调试器可利用该信息

gdb

在平时Windows下我们使用想vs2019这样的话编译器时都会有一个调试的功能,让我们发现代码的错误之处。

同理在Linux下也有,这个调试工具就叫gdb,首先我们要安装一下gdb

sudo yum install gdb

安装好之后当我们直接运行gdb调试我们的可执行代码时会发现出问题了

image-20221029161043362

因为程序会分为两个版本,一个是Debug版,一个是Release版

在debug模式下我们才可以对代码进行调试,但是Linux默认的模式是release,所以在生成可执行文件时需要加上-g选项,让它生成的可执行程序是debug模式才可以进行调试

image-20221029161431544

自此我们才算是正式进入到了gdb中

那么对于gdb而言也有属于自己的指令

  1. list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。
  2. list/l 函数名:列出某个函数的源代码。
  3. r或run:运行程序。
  4. n 或 next:单条执行。
  5. s或step:进入函数调用
  6. break(b) 行号:在某一行设置断点
  7. break 函数名:在某个函数开头设置断点
  8. info break :查看断点信息。
  9. finish:执行到当前函数返回,然后挺下来等待命令
  10. print§:打印表达式的值,通过表达式可以修改变量的值或者调用函数
  11. p 变量:打印变量值。
  12. set var:修改变量的值
  13. continue(或c):从当前位置开始连续而非单步执行程序
  14. delete breakpoints:删除所有断点
  15. delete breakpoints n:删除序号为n的断点
  16. disable breakpoints:禁用断点
  17. enable breakpoints:启用断点
  18. info(或i) breakpoints:参看当前设置了哪些断点
  19. display 变量名:跟踪查看一个变量,每次停下来都显示它的值
  20. undisplay:取消对先前设置的那些变量的跟踪
  21. until X行号:跳至X行
  22. breaktrace(或bt):查看各级函数调用及参数
  23. info(i) locals:查看当前栈帧局部变量的值
  24. quit:退出gdb

接下来我们一个一个看

首先显示我们的代码,需要用到l指令

image-20221029161748856

该指令后面也可以加上行号或者函数名,显示指定内容

接下来我们需要为程序设置一个断点,如果不设置直接执行程序就会跑完,这是要用到b + 行号指令,设置好之后我们可以通过info b查看我们已经设置好的断点

image-20221029162144207

还可以通过d + 序号删除对应的断点,注意这里的序号不是行号,而是我们设置好断点后每个断点通过info b查看之后每个断点对应的序号

image-20221029162403468

设置好断点后此时我们就可以运行起来了,输入r运行程序(对应VS2019F5),如果执行到某行,该行有函数我们想进入函数调试就需要输入s(对应vs2019的F11),不想进入函数就输入n(对应vs2019F10)

image-20221029163241939

image-20221029163327344

那么现在如果我进入到函数里面了,但是函数里有千次的循环怎么直接执行完函数退出呢,需要输入finish

image-20221029163515058

那么当我们进入到一个函数时,我们查看函数的变量值和变量的地址,这时候需要输入p + 变量名p + &变量名

image-20221029163746214

如果想每次执行完一条语句就显示出想查看的变量值,就需要用display + 变量名,不想看了想删除掉就使用undisplay + 变量名

image-20221029164018697

剩下的一些可以自己去探索了,在上面所讲的都是一些比较常用的。

总结

学习到这里已经是可以再Linux下进行代码编写了

接下来就得学习一些系统底层的知识了

每天进步一点淡👍

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值