gdb、make/makefile学习心得

gdb、make/makefile学习心得

gcc/g++

预处理的博客
在学习C语言的时候曾经讲过一个代码 是 如何变成一个可执行程序,Linux操作系统下的程序编译 和 vs环境下的步骤是一模一样的,但是Linux对代码的预处理的每一步都是可见的(每执行一步都会生成一个文件),下面来了解一下 编译的指令吧!
在这里插入图片描述
首先我们要了解一下Linux下的的编译器gcc和g++,这两个编译器负责预处理的全部过程,将我们的程序 编译 、链接最后转换成机器能听懂的二进制语言。我们可以通过指令 来生成预处理的每一步的文件,指令格式为:
gcc/g++ [选项] 要编译的文件 [选项] [目标文件]

  • 预处理阶段
    gcc -E [要预处理的文件名.c] -o [生成的文件名.i] 这时候就会生成预处理后的文件,你可以通过vim查看该文件,预处理 阶段编译器干了什么如上图⬆,一般把预处理阶段生成文件的后缀写成.i

  • 编译
    gcc -S [要编译的文件名.i] -o [生成的文件名.s] 这时候把编译结束后生成的文件输出,通过vim查看该文件会发现,代码全为汇编语言, 一般把编译阶段生成文件的后缀写成.s

  • 汇编
    gcc -c [要汇编的文件名.s] -o [生成的文件名.o] 这时候把链接结束后生成的文件输出,这时候生成的是二进制文件,一般把汇编阶段生成文件的后缀写成.o

  • 链接
    gcc [要链接的文件名.o] -o [生成的文件名.exe] 这时候就要进行文件链接,这里[要链接的文件名.o] 可能不止一个,文件和文件也可以链接,如果只有一个文件那么就会和库里的函数进行链接,例如:你调用了printf函数,但是你并没有在代码写出函数的定义,printf函数定义在你引用的库里面,这时gcc就回去/usr/lib路径底下取查找 是否存在包含该函数的库。这里介绍一下常见的函数库的分类:
    1. 静态库:静态库是指在编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行的时候也不再需要库函数
    2. 动态库:在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库里的函数,这样可以节省操作系统的开销
    gcc默认生成的二进制程序是动态连接的!

如果你想直接想生成可执行文件 可以gcc [要编译的文件] -o [生成的文件名] 这里也可以简化写成gcc [要编译的文件] 生成的可执行程序的名字默认是a.out

文件名问题

前面在学Linux的时候我们知道Linux是不以文件后缀来定义一个文件的属性,而是通过属性列 第一列的字母来确定文件的类型!
在这里插入图片描述
但是注意 gcc/g++编译器对文件后缀是识别的! 如果你的文件不是以.c .cpp 结尾,编译器是不识别的!

gdb

vs编译器 对代码有调试功能,Linux也可以通过 gdb 对文件进行调试 ,但是调试的过程肯定没有在vs2019条件下调试的舒服。

首先我们gdb调试的是文件的debug版本,而我们通过gcc [要链接的文件名.o] -o [生成的文件名.exe] 生成的默认是release版本,不能直接使用gdb调试。所以我们要生成 debug 文件, 只需要gcc [要链接的文件名.o] -o [生成的文件名.exe] -g 加一个-g就可以了。

进入和退出 gdb

  • 进入:gdb 可执行文件 (一定是debug!!!不然会报错)
  • 退出 : qctr d

基本调试操作

以这个程序为例:

  • list :显示源代码,接着上次的位置向下显示10 行,也可以使用 l 行号查看特定行的代码
  • r 或 run 从头开始调试,相当于vs中的f5
  • n 或 next:逐过程调试 相当于vs里面的 f10
  • s 或 step:逐语句调试,相当于vs里面的f11
  • break 行号:在某一行设置断点
  • break 函数名 :在某个函数开头设置断点
  • info break:查看已经设置断点的信息
  • finish:将当前非main函数执行完
  • continue:将程序从当前位置连续执行直到下一个断点或程序结尾,注意r是从开头开始执行,也就是你执行到某个位置,用r会从会从开头执行直到第一个断点
  • delete breakpoint n :删除序号为n的断点,如果没设置n,则是删除所有断点
  • display 变量名/undisplay 变量编号:设置或取消对变量的监事,这里对变量的监视是伴随着整个程序运行的过程

make/makefile

如果你用gcc和g++编译器进行多文件编译,你会发现如果你对其中一个文件进行修改,你就要面临着 对所有文件进行链接,并删除修改前的文件,如果一个项目足够大,有十几二十个文件,那么修改后编译的成本就会很高,这时就会用到make和makefile,makefile定义了一系列规则来指定,那些文件需要先编译,哪些文件需要后编译,那些文件需要删除,实现了编译的自动化,一旦写好后只需要输入make 命令就可以自动化编译!

在这里插入图片描述
所以: 使用make指令的第一件事 就是创建一个makefile 使用指令touch makefile

依赖关系:

在这里插入图片描述

只要能写成上面式子的,就是有依赖关系的,所以我们还可以得到几个依赖关系啊:
可执行程序 ——> .o .o ——> .s .s——>.i .i——>.c

如何写一个makefile

  • 生成文件部分
目标文件:目标文件依赖的文件(也就是能通过依赖关系式子生成目标文件的文件)
	依赖关系的式子

这里注意:

  1. 冒号右边的文件一定是存在的,或者说可以通过下面的依赖关系式子推导出来的!
  2. 依赖关系的式子开头的空格最好用tab键打出来,否则不识别!
  3. 冒号右边的文件可能不止一个文件

例如:我有一个文件
在这里插入图片描述
那么我编写的 makefile 生成可执行程序的代码就应该是:
在这里插入图片描述
这时在命令行输入make就会生成一个t1的可执行程序

我们也可以把这个依赖关系写的更加细节一些:
这个依赖关系有点像递归过程:t1 依赖于 test1.o ,但是test1.o不存在,于是继续向下找,发现test1.o 依赖于 test1.s…但是最后一定会找到一个已经存在的文件 (test1.c)也就是跳出递归的条件,这时候就开始回溯,最后推出t1
在这里插入图片描述

  • 清除文件部分——clean
.PHONY:clean
clean:
	rm -rf 需要删除的可执行文件 、中间生成的文件

这里的.PHONY 把clean定义成了一个伪目标,使他总是可以执行,否则会宝xxx is update
在这里插入图片描述

多文件的makefile

在这里插入图片描述

这里可以看出 test1 中调用了 test2 和 test3 中的函数,如果要执行test1就要链接test2和test3文件,所以makefile文件应该这样写:
在这里插入图片描述
这里目标: 目标文件 可以分别用 $@$^替代
等价于:gcc test -o test1.c test2.c test3.c
最后执行make 就生成了一个test可执行程序

多个文件在一个makefile分别编译

假如现在有这么一个情况,你有两个代码例如:test4.c test5.c ,你想分别生成两个可执行程序test4 test5,这时候makefile应该怎么写?

在这里插入图片描述
那我们如果直接把两个代码的依赖关系直接写上去的话,例如:
在这里插入图片描述
由于makefile只会执行第一个依赖关系,所以如果像上面那样写,最终就只会生成一个test5可执行程序,所以我们可以定义一个伪目标all,让这个目标依赖于要生成的可执行程序,由于可执行程序没有被生成,所以make会继续向下寻找依赖关系,就会执行上面我们写的两个依赖关系

在这里插入图片描述
最后的结果如下:
在这里插入图片描述

  • 15
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值