gdbmake/makefile学习心得
Posted 正义的伙伴啊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gdbmake/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!!!不然会报错) - 退出 :
q
或ctr d
基本调试操作
以这个程序为例:
list
:显示源代码,接着上次的位置向下显示10 行,也可以使用l 行号
查看特定行的代码r 或 run
从头开始调试,相当于vs中的f5n 或 next
:逐过程调试 相当于vs里面的 f10s 或 step
:逐语句调试,相当于vs里面的f11break 行号
:在某一行设置断点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
- 生成文件部分
目标文件:目标文件依赖的文件(也就是能通过依赖关系式子生成目标文件的文件)
依赖关系的式子
这里注意:
- 冒号右边的文件一定是存在的,或者说可以通过下面的依赖关系式子推导出来的!
- 依赖关系的式子开头的空格最好用
tab
键打出来,否则不识别! - 冒号右边的文件可能不止一个文件
例如:我有一个文件
那么我编写的 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会继续向下寻找依赖关系,就会执行上面我们写的两个依赖关系
最后的结果如下:
以上是关于gdbmake/makefile学习心得的主要内容,如果未能解决你的问题,请参考以下文章
Linux环境基础开发工具的使用(yumvimgccg++gdbmake/Makefile)
LinuxLinux环境基础开发工具的使用 ———(yumvimgcc&g++gdbmake/Makefile进度条 git)