Linux环境基础开发工具使用——gcc/g++、make/makefile and gdb

         本篇将主要介绍三个 Linux 环境基础开发工具,gcc/g++、make/makefile and gdb。其中在介绍 gcc/g++ 的使用时,重点讲解了生成可执行文件的四个步骤:预处理、编译、汇编和编译,然后介绍了 gcc/g++ 的常用指令。介绍 make/makefile 的时候,我们分别介绍了 makefile 文件内部的原理、执行顺序、其中的内置符号以及在 makefile 中可以定义的变量。最后对于调试器 gdb 的使用,详细的介绍了 gdb 中常用的指令。目录如下:

目录

1. Linux编译器---gcc/g++的使用

1.1 预处理、编译、汇编和链接

1.2 gcc 所有选项

2. make/makefile

2.1 makefile文件内部原理

2.2 makefile对最新可执行程序默认不执行原理

2.3 makefile文件中的内置符号

2.4 makefile中的执行顺序

2.5 makefile中定义的变量

3. Linux调试器--gdb

3.1 gdb 的使用

进入和退出gdb:

查看我们的代码:

运行代码:

打断点和取消断点以及查看断点:

逐过程和逐语句:

查看变量的值:

直接运行至断点处:

运行结束所在函数:

运行到指定行:

1. Linux编译器---gcc/g++的使用

        在进行介绍Linux编译器的使用前,我们需要先简单地介绍一个可执行文件的生成过程,可执行文件的生成过程主要分为:预处理、编译、汇编和链接。当然对于这四个过程我在C语言篇有更为详细的介绍,但在这里还是需要结合Linux系统结合的讲一下,如下是详细的四个过程的blog:预处理/预编译详解(C/C++)-CSDN博客编译与链接(C/C++)-CSDN博客

1.1 预处理、编译、汇编和链接

        在预处理阶段,主要进行的操作为:预处理、宏替换、条件编译以及头文件展开。在编译阶段进行的操作为:将预处理后的文件进行语法检查,检测语法是否存在错误, 然后进行词法分析,然后将文件语言变成汇编语言。在汇编阶段,将汇编语言翻译为二进制目标文件。最后链接将各个二进制目标文件组合在一起形成可执行文件。

        以上就是这四个过程的主要功能的概括。

预处理

        若想要进入预处理阶段,我们需要使用指令:gcc -E XXX.c -o XXX.i

        该命令的意思为:从现在开始进行程序的翻译,预处理完成就停下。

        执行完该命令后生成的 .i 文件就是预处理之后的源文件,如下:

        右图就是形成的 .i 文件,我们可以发现,在 .i 文件中将注释的代码给删除了,然后将头文件展开了,还有将定义的宏给替换掉了。

        以上就是对 .c 文件进行预处理,但是当我们还需要人为的进行条件编译呢,我们可以选择使用条件编译指令进行对部分代码进行划分,如下:

        我们在代码中并未定义 V1 和 V2,但是当我们想要执行对应的指令时,我们可以进行指令:gcc -DV1=1 XXX.c 这就可以在外部将 V1 进行定义,就可以执行对应位置的代码了。

编译

        若我们想要进入编译阶段的代码,我们可以执行指令:gcc -S XXX.i(XXX.c) -o XXX.s

        执行完该指令之后,我们就可以生成后缀为 .s 的文件了,如下:

        右图就是编译后的代码,将预处理后的代码处理为汇编代码。

汇编

        若我们想进入经过汇编处理过后的文件,我们可以输入指令:gcc -c XXX.s(XXX.c) -o XXX.o

        输入以上指令之后,就可以生成对应的二进制文件了,如下:

        右图就是生成的二进制文件,但是就算是生成了二进制文件,也还不满足运行的条件,因为还未经过链接。

链接

        最后还是需要通过链接来形成可执行文件,需要输入指令:gcc XXX.o(XXX.c) -o [filename]

        执行以上命令之后,就可以生成对应的可执行文件啦。

        但是在链接过程未只有这些知识。在链接过程中,我们还需要将 .o 文件与标准库进行链接,去调用标准库内的函数。其中标准库分为静态路和动态库,所以我们在链接时可以选择静态库链接也可以选择动态库链接,分别被称为动态链接静态链接

        对于动态库来说,其实也叫共享动态库,其中的文件一旦缺失,所有的动态链接这个库的程序都无法执行了,但是好处就在于链接这个库的之后,生成的可执行文件容量较小,占用内存较小,节省资源

        对于静态库来说,在链接的时候,把库中的函数方法,拷贝到对于的可执行文件中去,现在的可执行文件就不在依靠其他的库,但是缺点就是容量太大,占用内存太大,浪费资源

        (gcc默认形成的可执行文件采用的是C动态库,默认动态链接)

如下:

        如图所示,生成的静态链接文件比动态链接的文件大100倍左右。

静态链接的场景

        当部署一个文件时,需要各种动态库时,会存在有的有动态库有的没有动态库,这就会导致有的电脑上就部署失败,所以可以在部署的时候将文件进行静态链接,然后在部署。

1.2 gcc 所有选项

        gcc 所有的指令选项如下:

-E // 只激活预处理,不生成文件,需要将其重定向到另一个输出文件中
-S // 编译到汇编语言不进行汇编和链接
-c // 编译到目标文件
-o // 文件输出到 文件
-static //生成的文件采用静态链接
-g 生成调试信息。
-shared // 表示尽量使用动态库,所有生成的文件较小,但前提是有动态库
-O0
-O1
-O2
-O3 // 编译器优化选项的四个级别,-O0没有优化,-O1为缺省值,-O3优化级别最高
-w  // 不生成任何警告信息
-Wall // 生成所有警告信息

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

        g++ 和 gcc 的使用差不多,但使用 g++ 之前需要查看是否安装了 g++。

        若不能生成静态文件,则需要安装静态库:

sudo yum install glibc-static

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

2. make/makefile

        通常在一些较大的工程中,源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile 定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译以及哪些文件需要需要重新编译,甚至进行更复杂的功能操作(因为在makefile文件外使用的指令都可以加入在makefile中)。

        make 则是一个命令工具,用来解释 makefile 中指令的命令工具,一般来说,大多数的   IDE 都有这个命令。比如:Visual C++的nmake,Linux下GNU的 make。

        make 是一个命令。makefile 是一个文件,两个搭配使用,完成项目自动化构建。

2.1 makefile文件内部原理

        我们先创建一个当前代码文件路径下创建一个 makefile 文件,然后使用 vim 在文件内写下如下指令:

        现在将对以上格式进行讲解。

        1. 第一行被称为依赖关系,冒号左边的被我们称为目标文件,冒号右边的被称为依赖文件列表,若依赖文件列表存在多个文件,每个文件之间按照空格分隔开。

        2. 第二行被称为依赖方法,在写这一行之前需要一个 tab 键,其实这一行就是我们需要执行的命令。

        3.  第四行是一种特殊的依赖关系,由于冒号右边没有任何文件,所以不依赖任何文件,同时也表明依赖文件列表可以为空

        4. 第五行表示的依赖方法和第四行的依赖关系是一种特殊情况。

        5. 第三行的 .PHONY 作为一个关键字,使用方法为 .PHONY 目标文件,表明目标文件对应的方法总被执行,不会被提示。如下:

        如上,当我们执行过 make 指令之后显示已经生成 my.exe 文件,不需要更新,但是当我们在 makefile 中加入 .PHONY 修饰过后,就不会在显示了。

        依赖关系依赖方法之间的关系:在生成可执行文件之前。需要先报名依赖关系,但是光有依赖关系还不可以,还需要依赖关系之家的依赖方法,这样配合才能生成可执行文件。

当我们执行指令时:

        如上图,当直接执行 make 指令时,执行的是第二行的指令,当我们想要执行清除功能时,必须执行 make clean。但是当我们执行 make my.exe 时,也可执行第二行的指令。

        直接 make 生成可执行文件,是因为当 make 执行时,默认从上至下开始扫描,执行并形成第一个目标文件(若是clean在第一行,那么默认执行clean)。 若想要执行其他行的指令时,要执行 make 目标文件。

2.2 makefile对最新可执行程序默认不执行原理

        由上可知,当目标文件未被 .PHONY 修饰时,执行 make 时,若之前已经生成,编译器就不在会进行编译了。

        这是编译器为了提高效率设定的程序,在我们平时执行较小文件时,不太会有感觉,但是当执行的是一个非常大的文件列表时,存在几千个,那么每一次编译都会消耗很多的时间,若不小心再次执行 make 执行,这会严重的消耗效率。

那么对最新可执行程序默认不执行的原理是什么呢?

        下看下图:

        如上图,我们使用指令查看文件的 acm 时间时间,其中 Modify 时间表示最近修改一次的文件,我们发现可执行文件的 M 时间在可执行文件的 M 时间后面,这说明在可执行文件创建之后,源文件没有被修改过,说明可执行文件为最新的文件,再一次从源文件生成可执行文件没有意义,所以编译器默认并不会再一次生成可执行文件。

        当我们更新了源文件的 M 时间之后呢,再一次编译时就会生成可执行文件。如下:

        如上所示,当我们使用指令更新源文件的 M 时间之后,就可以直接生成可执行文件了(touch XXX:更新XXX的 acm 时间)。所以同时来说关键字:.PHONY 实现的原理就是在执行指令前,禁止系统对比源文件和可执行文件的 M 时间,直接执行就可以了。

2.3 makefile文件中的内置符号

        在 makefile 文件中,还存在其他一些内置符号,如下:

        对比上文中 makefile 中的命令,我们将 my.exe 替换为了 $@,然后将 code.c 替换为了 $^ 符号,其中 @ 符号代表目标文件,^ 符号表示整个依赖文件列表,而 @ 符号表示取出 ^ 和 $ 中的内容。

2.4 makefile中的执行顺序

        在 makefile 中的执行顺序中,默认向下扫描,直到扫描到全部需要的文件时,就会开始执行指令。如下图:

        1. 首先先按照最开始的顺序执行命令,从下开始扫描,要生成 code.exe 文件,需要 code.o 文件,没有 code.o 文件,生成 code.o 文件需要 code.s 文件,……,直到需要 code.c 文件,当前路径下恰好有 code.c 文件,于是开始执行。

        2. 将 makefile 文件中的顺序打乱,除 code.exe 文件还排在第一个,当我们执行 make 指令时,发现系统提示已经生成了目标文件,不会在生成可执行文件。

        3. 当我们再次将顺序打乱,不过这次打乱的顺序将目标文件为 code.s 的文件放在了第一个,所以执行的命令只有两行。

        出现以上的情况的原因为:make 指令执行时,以排在第一个目标文件为最终目标文件,当第一个目标文件生成之后,就不会在继续执行命令了,所以一般将想要生成的目标文件放在第一行。(若在执行使,发现找不到需要依赖的文件也找不到可以生成依赖的文件的命令,这时候就会报错)

2.5 makefile中定义的变量

        在 makefile 文件中,也是可以进行变量定义的,便于我们处理指令管理。如下:

        在图中,我们定义了两个变量,分别为 des 和 src (注:在这样的定义中,中间不能加空格),然后在依赖关系这一行将原来的目标文件和依赖列表替换为了 $(des) : $(src) ,这样其实就是可以表示依赖关系,其中 des 和 src 变量相当于宏。我们还在指令之前加入了一个 @ 符号,@ 符号的作用为执行指令之后,不要在屏幕上打印指令,@echo “字符串” 的作用为执行命令之后在屏幕上打印我们想要的字符。

        那么这样定义有什么作用呢?

        当我们想要执行 make 指令生成其他文件的目标文件的时候,我们就可以直接修改 des 和 src 的值,便于我们管理 makefile 文件,之后修改也只需要修改一个对应的值,也不用重写一个 makefile 文件。

3. Linux调试器--gdb

        在 Linux 中,也可以对我们的代码进行调试,我们需要使用我们的调试器 gdb,但是调试器 gdb 只能调试 Debug 模式下的代码,而 gcc 默认执行的代码为 Release 版本,所以假若想要对程序进行调试,我们需要生成 Debug 版本的代码。生成 Debug 可执行文件的代码如下:

        要生成 Debug 版本的可执行文件,我们只需要在平时的 gcc 命令之后加入 -g 指令。由上图所示,我们的 Debug 版本下的可执行文件比 Release 版本下的可执行文件要大一些,那是因为编译器在文件中加入了一些调试信息。

        注:使用 gdb 调试之前,可以先将屏幕分屏,打印出代码,便于我们在调试的时候观察代码,如下:

3.1 gdb 的使用

        gdb 的使用同样使用指令的形式进行使用,将在下面介绍一些最常用的调试指令。使用 gdb 之前,需要先配置 gdb:sudo yum install -y gdb

进入和退出gdb

        进入和退出 gdb 的指令为 gdb Debugfilename 和 quit(q):

        如上,当我们输入 gdb Debugfilename 的时候,加载了调试信息并进入调试模式。当我们输入 quit 或 q 指令的时候,就是退出调试模式。

查看我们的代码

        查看我们的代码,使用的指令为 list,或者简写 l 指令。如下:

        对于 list(l)指令的使用,可以在 l 后面加上行号,表示将要看哪一行,也可以在 l 后面加上 filename:函数名 查看函数名附近的代码,一般只查看十行的代码,并不会直接将所有的代码打印出来。若我们想接着查看后面的代码,我们可以直接回车,因为 gdb 会记录我们上一次执行的命令。若想查看全部代码,我们只需要输入 l 0 后,一直回车,就可以查看全部代码。

        查看的代码为想要查看代码行数的前面几行和后面几行。

运行代码

        在 gdb 中想要运行整个代码,或运行到断点处,需要的指令为 run 或者 r。如下:

        如上所示,当我们执行完代码之后,gdb 还会输出代码的执行情况,是否为正常退出。

打断点和取消断点以及查看断点

        在 gdb 中对代码进行打断点的命令为: b 行号 或 b 文件名 : 行号 或 b 文件名 : 函数名,其中 b 为 breakpoint 的简写,输入 info b 查看断点信息,如下:

        如上,输入如上指令之后,我们就可以在调试的时候打断点了,若输入的指令为 文件名 : 函数,将默认打断点到函数之后的一行。输入 info b 之后,输出了每个断点的编号以及所占的文件,所在的函数中。

        断点的关闭和打开:我们在上面的蓝框中,发现第四行 Enb(enable) 就是表示当前断点是否开启,也就是是否有效,我们可以使用指令:disable 断点编号,若想要打开断点,输入指令:enable 断点编号。如下所示:

        断点需要和 run(r)指令配合使用

        当我们想要删除断点的时候,需要的指令为: d 断点编号,不能将断点编号输入为行号,这样并不能将断点删除,如下:

逐过程和逐语句

        逐过程的调试为在当前函数中一句一句的执行,遇到函数不会进入到函数中去;逐语句为一句一句的执行,在当前函数体中遇见了一个函数,也会跳转到另一个函数中的一句一句的执行。

        对于逐过程语句,输入指令 next 或 n,如下:

        输入 n(next)指令之后,就会一句一句的开始执行,执行时,还会将执行的语句和行号打印出来,对于空行而言,直接跳过。

        逐语句调试,需要输入指令 step 或 s,如下:

        如图所示,当输入 s(step)指令之后,就可以进入函数中进行一句一句的执行了。

查看变量的值

        在调试的过程中,我们有时候需要观察变量的值,便于我们发现问题,在gdb中,存在两种查看变量值的方法,一种为只查看一次,另一种为一直查看变量的值。只查看一次输入指令 print 或者 p 变量名,一直查看变量输入指令 display 变量名,如下: 

        如上所示,执行 p 指令之后,只能查看一次变量,输入 display 之后,在每执行一句之后,都会常显示这个变量的值。想查看变量地址的话,只需要在变量前加一个取地址符号。

        当我们想要取消常显示的时候,可以输入指令 undisplay 变量编号,如下:

        不能在 display 后边加变量名,而是需要加变量编号。

直接运行至断点处

        当我们在调试程序的时候,经常有可能需要使用跳转断点来锁定问题出在什么地方,跳转到下一个断点的指令为 continue 或 c,如下:

        当执行指令之后,直接跳到了下一个断点。

运行结束所在函数

        在某个函数中,当我们已经不想继续在这个函数中继续运行下去了,我们想要直接跳出这个函数,我们可以输入指令 finish,如下:

        当运行结束之后,还会将函数的返回值给打印出来。

运行到指定行

        当进入到一个很长的循环之内时,假若我们想要直接跳出这个循环,其中一种方式就是打断点,然后运行到断点处;另外一种方法就是使用 until 指令跳转到对应的行号处,如下:

        以上就是在 gdb 调试器中常用的指令。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值