资源描述
第,5,章,Linux,编程环境,文本编辑器,Vim,GCC,编译器,GDB,调试器,Make,工程管理器,文本编辑器,Vim,GCC,编译器,点击查看本小节知识架构,GDB,调试器,点击查看本小节知识架构,5.2,5.3,5.1,点击查看本小节知识架构,Make,工程管理器,点击查看本小节知识架构,5.,4,掌握,掌握,掌握,掌握,学习目标,掌握,Vim,编辑器的使用方法,1,掌握,Make,工程管理器的使用方法,4,2,掌握,GCC编译器的使用方法,3,掌握,GDB调试器的使用方法,对于程序开发者来说,熟练使用Linux操作系统开发工具是编程开发的前提,因此本章将针对Linux操作系统中常用的编程开发工具(文本编辑器、程序编译器、调试器、Make工程管理器)分别进行介绍。通过,学习,环境配置和相关的使用技巧,读者,应,提升对原始操作系统的环境搭建能力,熟练使用操作系统工具进行开发工作。,5.1,文本编辑器,Vim,5.1.1,文本编辑器简介,返回目录,5.1.2,Vim,的安装与配置,5.1.3,Vim,的工作模式,5.1.4,Vim,按键说明,5.1.1,文本编辑器简介,5.1,文本编辑器,Vim,文本编辑器在操作系统中扮演着十分重要的角色,不论是配置系统文件还是编写程序代码,,,都需要借助于文本编辑器来完成。不同的操作系统中存在不同的文本编辑器,如TextMate(Mac,操作系统,)、Notepad+(Windows,操作系统,)等。虽然这些文本编辑器都是用来编辑文件,的,,但其内部设计的细节不尽相同,因此也形成了各自的特色。,Linux操作系统中有许多非常优秀的文本编辑器,按照其功能可以分为4类,分别为行编辑器、全屏编辑器、字符界面编辑器和图形界面编辑器。,(1)行编辑器:每次只能,处理,文本中的一行,使用较为不便。,(2)全屏编辑器:可以实现对整个屏幕的编辑,用户编辑的文件直接显示在屏幕上,从而解决行编辑器的不直观问题。,5.1.1,文本编辑器简介,5.1,文本编辑器,Vim,(3)字符界面编辑器:早期的编译器,运行在字符界面中,不支持鼠标操作。由于现在的服务器都运行在字符界面下,因此字符界面编辑器也十分重要。,(4)图形界面编辑器:操作方法与Windows,操作,系统中的记事本类似,同时提供了语法高亮显示功能。,大部分,版本,的,Ubuntu系统默认安装全屏编辑器Vim,以及图形界面编辑器gedit。这两种编辑器由于功能性强,与用户交互十分友好,因此使用较为普遍。,gedit是一种,在,GNOME桌面环境下兼容UTF-8的文本编辑器,同时具有语法高亮和编辑多个文件的功能,,为自由软件,。,5.1.1,文本编辑器简介,5.1,文本编辑器,Vim,图形界面编辑器gedit打开文件的界面如图所示。,Vim是vi,命令,发展,而成,的一种文本编辑器,具有代码补充、错误跳转等功能。Vim的设计理念是命令的组合。各种各样的文本间移动、跳转命令与其他普通模式的编辑命令灵活地组合使用,更加高效地进行文本编辑,因此这种编辑器被程序员广泛使用。,5.1.1,文本编辑器简介,5.1,文本编辑器,Vim,全屏编辑器Vim打开文件的界面如图所示。,5.1.2,Vim,的安装与配置,5.1,文本编辑器,Vim,1.Vim的安装,如果读者使用的Ubuntu系统中没有Vim编辑器,则需要自行安装并配置。检测、安装Vim的过程,示例代码参考教材,5.1.2,节,。,例显示的结果表示Vim编辑器安装成功。此时的Vim编辑器已经可以实现文本的编辑,但是由于缺少一些人性化的配置,其用户体验,不佳,,因此需要对Vim编辑器进行一些配置工作。,5.1.2,Vim,的安装与配置,5.1,文本编辑器,Vim,2.Vim的配置,未经过配置的Vim编辑器,使用,不方便且不美观。如图所示,使用未,经过,配置的Vim编辑器编写一个C语言程序代码。,Vim 的全局配置一般在文件“/etc/vim/vimrc”或者“/etc/vimrc”中,且对所有用户生效。如果只是针对特定的用户进行个性配置,则选择为文件“/.vimrc”(用户主目录下的隐藏文件.vimrc)进行配置。,5.1.2,Vim,的安装与配置,5.1,文本编辑器,Vim,本小节选择文件“/.vimrc”进行配置演示,需要声明的是,配置文件内容仅供参考。读者如果直接复制使用可能会因为未安装Vim相关插件而出现警告提示,此时只需要将提示出错的具体代码行注释掉即可,注释代码使用符号“”(英文双引号)即可。使用gedit打开新文件.vimrc(命令为gedit/.vimrc),,示例代码参考教材,5.1.2,节。,例为配置文件添加的内容,读者不必过多解读,如需要个性定制可以根据自身需求在网络中搜索Vim配置方案,参考优秀示例进行修改。Vim的一些配置依赖Vim的插件产生作用,这些插件一般保存在“/.vim”目录中。,5.1.2,Vim,的安装与配置,5.1,文本编辑器,Vim,读者在使用网络中其他程序开发者的.vimrc配置文件时,如需要具体的Vim插件,可以选择去其个人的github仓库中下载,或者使用Vundle插件管理器进行安装。,保存例中的配置文件,关闭终端,再重新打开,终端,配置,即可生效。图所示为配置生效后编写代码的新效果,,Vim,自动实现添加文件信息显示,并且可以实现Tab键自动补齐。,5.1.3,Vim,的工作模式,5.1,文本编辑器,Vim,Vim的工作模式有3种,分别为命令模式、输入模式、底行模式。,1.命令模式,当使用,命令,vim打开文件,这个初始状态就属于命令模式。,如例所示,,,打开文件README。,在这种模式下,用户可以使用按键,移动,光标,完成文本的字符甚至整行的删除、复制、粘贴等,操作,。此状态下按键输入会被Vim识别为命令,而非字符,打开文件进入命令模式,,,如图所示。,5.1.3,Vim,的工作模式,5.1,文本编辑器,Vim,2.插入模式,在命令模式下,无法实现对文件的编辑操作,因此需要,进行,模式切换。在命令模式下按下,输入,i、I、o、O、a、A、r、R任意一个字母键,即可,进入插入模式,此时用户对文件进行操作,可参考,Windows记事本,操作方法,。如果需要切换回命令模式,按Esc键即可。Vim的插入模式如图所示。,5.1.3,Vim,的工作模式,5.1,文本编辑器,Vim,3.底行模式,在命令模式下,按,快捷,键“Shift+:”或“Shift+/”即可进入底行模式。在此模式下,可以实现查找、存盘(保存文件)、替换字符、保存退出等一系列操作。Vim底行模式如图所示。,5.1.3,Vim,的工作模式,5.1,文本编辑器,Vim,以上,3,种模式可以根据用户的需求切换使用,切换的方式如图所示。,5.1.4,Vim,模式按键说明,5.1,文本编辑器,Vim,命令模式下,用户可根据情况灵活使用各种按键进行文本的快捷操作,其常用的按键及说明如表所示。,5.1.4,Vim,模式按键说明,5.1,文本编辑器,Vim,使用某些特定的字符按键即可将Vim从命令模式切换到输入模式,其常用的按键及说明如表所示。,5.1.4,Vim,模式按键说明,5.1,文本编辑器,Vim,底行模式,常用的,按键及说明如表所示。,5.2 GCC,编译器,5.2.1,GCC,编译器简介,返回目录,5.2.2,GCC,编译流程,5.2.3,GCC,编译选项,5.2.4,GCC,编译器版本切换,5.2.1,GCC,编译器简介,5.2,GCC,编译器,GCC(GNU Compiler Collection)是一款编译语言编译器,此项目最早由GNU计划的发起者理查德 斯托曼开始实施。,第一版,GCC,于,1987年,发行,,最初的GCC,代表,GNU C Compiler,即GNU的C语言编译器。后来经过不断地发展,GCC,适应了,C+、Objective-C、Java、Go等更多编译语言。GCC最重要的特点为实现跨硬件平台编译,即,可,在当前的CPU平台上为其他体系结构的硬件平台(ARM、MIPS、X86、PowerPC)开发软件,目前这一方式被广泛应用于嵌入式开发中。,GCC编译器的工作目的就是将开发者编写的语言代码变成可以被机器识别的二进制码。,一个完整的编译器主要由以下4部分组成,,它们,可称为编译器的主要组件。,5.2.1,GCC,编译器简介,5.2,GCC,编译器,(1)分析器:将源程序代码转换为汇编语言。,(2)汇编器:将汇编语言的代码转换为CPU可以执行的字节码。,(3)链接器:将汇编器生成的单独的目标文件组合成可执行的应用程序。,(4)标准C库:提供对核心函数的支持,如果应用程序使用到C库中的函数,C库就会通过链接器与源代码连接,,,来生成最终的可执行程序。,5.2.1,GCC,编译器简介,5.2,GCC,编译器,使用,Vim编辑器,编辑,C语言代码。,5.2.1,GCC,编译器简介,5.2,GCC,编译器,保存例中的文件,通过GCC编译器进行编译,如例所示。,例中,将C语言代码,文件,test.c通过gcc,命令,执行编译,生成编译后的执行代码a.out,执行a.out得到程序的运行结果。,使用GCC编译器编译代码时,用户可直接使用命令gcc,不附加任何选项,指定需要编译的文件名即可。,5.2.2,GCC,编译流程,5.2,GCC,编译器,例中的示例代码(体积小),虽然编译时间很短,但是从源,代,码test.c到执行代码a.out,总共经历了4个编译必不可少的步骤,分别是预处理(Pre-Processing)、编译(Compiling)、汇编(Assembling)、链接(Linking)。如图所示。,5.2.2,GCC,编译流程,5.2,GCC,编译器,1.预处理,在预处理阶段,GCC,主要处理带“#”的指令,如#include(头文件)、#define(宏定义)等,并删除注释、添加行号和文件名标识。例,如,例,中的代码,在预处理的阶段将把包含的头文件stdio.h添加进来(解析头文件),,然后,生成预处理文件test.i。,预处理可以通过GCC编译器单步编译实现,只需在命令gcc,中,添加选项“-E”即可。该选项的作用就是让编译器执行完预处理后停止编译过程。,5.2.2,GCC,编译流程,5.2,GCC,编译器,如例所示,单步执行编译(只执行预处理过程),生成预处理文件。其中“-o”表示指定生成的新文件的名称。,2.编译,编译阶段中,GCC,对,预处理文件进行词法分析、语法分析、语义,分析,,检查代码的规范性。确认无误后,GCC将代码翻译为汇编语言。同样,,,编译也可以使用GCC编译器进行单步操作。,添加,选项“-S”即可完成编译操作,而不会继续执行汇编处理。,5.2.2,GCC,编译流程,5.2,GCC,编译器,例单步执行编译(只执行编译过程),生成汇编文件。,由,C语言代码生成的汇编代码,参考教材,5.2.2,节,。汇编代码也是嵌入式,开发,的一部分,感兴趣的读者可以查询汇编代码中这些符号的意义。,5.2.2,GCC,编译流程,5.2,GCC,编译器,3.汇编,汇编阶段汇编代码转换为机器可以执行的指令。使用编译器进行单步操作,通过,添加,选项“-c”即可指定生成二进制的目标文件。例,中,生成二进制目标文件test.o。,5.2.2,GCC,编译流程,5.2,GCC,编译器,4.链接,链接是一个复杂的过程,包括符号地址确定、符号解析与重定位、指令修正等。链接阶段有一项重要的工作,,,就是链接库文件,程序代码中经常会出现一些函数接口的使用,这些函数并不需要开发者自己实现,其功能已经被写好并编译到函数库中,开发者只需要调用库函数即可。,函数库分为静态库与动态库两种。,对,静态库,而言,,编译链接时,会,把库文件代码加载到执行文件中,因此生成的文件体积较大,但运行时不需要库文件。动态库则刚好相反,在编译链接时并不会将库文件加载到执行文件,而是在程序执行时加载库文件。,5.2.2,GCC,编译流程,5.2,GCC,编译器,完成链接操作即可生成可执行文件,如例所示。,5.2.2,GCC,编译流程,5.2,GCC,编译器,当需要获取特定的编译文件时,可以考虑单步执行编译处理,也可以一次性执行多个步骤,如例所示,将源程序代码直接编译生成二进制的目标文件,本次编译经历预处理、编译、汇编3个阶段。读者也可以根据情况,灵活执行编译处理。,5.2.3,GCC,编译选项,5.2,GCC,编译器,GCC有很多,附加,选项可以使用,包括总体选项、警告,选项,、优化,选项,以及体系结构相关选项。其中常用的,附加,选项如表所示。,5.2.3,GCC,编译选项,5.2,GCC,编译器,表中的警告选项与优化选项在编译代码时经常被使用。如例所示,该程序第5行代码中定义的变量在整个程序代码中并未使用,但是并不影响代码的运行。,5.2.3,GCC,编译选项,5.2,GCC,编译器,对例所示的代码分别执行普通编译以及严格编译,如例所示。,从,输出结果可以看出,如果采用,普通,编译,则编译无误,并且执行成功;而如果添加“-Wall”进行严格编译,,,则出现警告,提示定义的变量a并未在代码中使用。严格编译在程序开发中建议经常使用,以,提高代码编写规范,优化代码质量。,5.2.4,GCC,编译版本切换,5.2,GCC,编译器,不同的编译器版本,在对源程序进行编译时,可能会产生不同的结果。例如,使用新版本的GCC链接旧程序时,由于旧程序的.o文件对函数的修饰,可能,与新版本不同,编译时可能会出现一些新的警告,。,不同版本的GCC配置的库也不尽相同,这,可能,导致文件编译时出现意想不到的错误。,鉴于上述情况,开发者在为操作系统搭建编译器时,一般会选择最适合当前开发的编译器版本,而非版本越新越好。,接下来将展示如何在Ubuntu系统中配置多个,版本的,GCC编译器版并切换使用,从而保证系统在任何时候都可以快速进行编译开发。,5.2.4,GCC,编译版本切换,5.2,GCC,编译器,(1)查看系统原装的GCC版本,使用命令“gcc-v”(C语言编译)或“g+-v”(C+语言编译),如例所示,gcc与g+的版本,号,一致,都为5.4.0。,5.2.4,GCC,编译版本切换,5.2,GCC,编译器,(2)安装其他版本的GCC,如例所示,选择在线安装GCC 4.8。,5.2.4,GCC,编译版本切换,5.2,GCC,编译器,安装完成后,查看当前系统中安装的所有gcc与g+版本。如例所示,当前系统支持4.8与5(5.4.0)两个版本。,(,3,)将下载的,4.8,版本的,gcc,与,g+加入候补名单,并设置优先级,优先级设置为100,如例所示。,5.2.4,GCC,编译版本切换,5.2,GCC,编译器,将系统中原有的5.4版本的gcc优先级同样也设置为100,避免在选择完使用版本之后系统恢复默认优先级的版本,,,如例所示。,g+的设置与例中展示的gcc一致,如例所示。,5.2.4,GCC,编译版本切换,5.2,GCC,编译器,(4)通过指令切换不同版本的GCC,如例所示,可直接输入序号(这里选择1)。,5.2.4,GCC,编译版本切换,5.2,GCC,编译器,g+与gcc切换方式一致,如例所示,直接输入序号选择(这里选择1)。,(5)检查gcc与g+版本,查看是否切换成功。如例所示,gcc与g+成功切换为4.8.5版本。,5.2.4,GCC,编译版本切换,5.2,GCC,编译器,结合以上步骤,读者可根据系统需求进行编译器的合理切换,完成编译工作。,5.3 GDB,调试器,5.3.1,GDB,调试器简介,返回目录,5.3.2,GDB,调试器的使用,5.3.3,GDB,基本命令,5.3.1,GDB,调试器简介,5.3,GDB,调试器,1.GDB调试器概念,GDB(GNU Symbolic Debugger)是GNU开源组织发布的一款程序调试工具。与Windows的IDE不同,GDB是纯命令执行,没有图形界面,但是其功能却比图形界面调试器更加强大。调试工作在产品研发中占有很重要的位置,一款产品从制定需求到成熟上线,可能需要做完成性测试,单元测试等,这些都离不开调试工具的使用。,GDB可以帮助用户,完成,查看程序的内部结构、,查看,自定义程序的启动方式、设置条件断点、单步调试源代码等各种调试工作。,5.3.1,GDB,调试器简介,5.3,GDB,调试器,2.GDB调试器展示,下面将通过一个示例展示GDB调试器的使用,编写一,段,完整的C语言示例代码。代码的功能本意为通过change子函数将变量a、b中的值进行交换,但实际的情况是change函数中的形式参数,(形参),a、b的值进行交换,与main函数中的实际参数a、b没有任何关系(形参a、b与实参a、b分别占有各自的内存地址,互不影响)。因此,最终的运行结果为交换前与交换后打印输出的a、b的值不会发生变化。本节将通过GDB调试器对例的代码进行调试,并查看程序在运行过程中变量的值,从而更好地解释交换失败的原因。,示例代码参考教材,5.3.1,节。,5.3.1,GDB,调试器简介,5.3,GDB,调试器,保存例所示的文件,使用GCC编译器进行编译。编译时需要添加“-g”选项,从而保证生成的可执行代码包含调试信息,否则无法使用GDB进行调试。具体如例所示。,GDB调试的目标为可执行文件。如例所示,使用命令“gdb+可执行文件名”即可启动GDB调试。,5.3.1,GDB,调试器简介,5.3,GDB,调试器,例所示的,调试,界面中,显示了GDB调试器的版本信息以及当前调试文件所在的绝对路径。在提示符“(gdb)”后可进行命令行输入,开始调试工作。如果用户对当前的输入命令不确定,可以,进行,查询,在命令行输入help即可显示命令帮助,如例所示。,5.3.2,GDB,调试器使用,5.3,GDB,调试器,1.查看文件内容,在调试代码之前,可以先通过GDB查看代码的中的内容(代码见例5-24)。在GDB命令行输入区域中输入list即可查看调试的文件。,示例代码参考教材,5.3.2,节,,一次性只能显示部分内容(默认10行),直到显示结束为主。,2.设置断点,设置断点在代码调试过程中十分关键。在代码的某一处设置断点后,程序运行到该断点处就会停止,即,只运行,断点之前的代码。使用命令break设置断点,其设置的方式有很多。,5.3.2,GDB,调试器使用,5.3,GDB,调试器,具体如表所示,。,5.3.2,GDB,调试器使用,5.3,GDB,调试器,在例所示的代码中,选择第7、8、9、19、21、24行设置断点。具体操作如例所示。,5.3.2,GDB,调试器使用,5.3,GDB,调试器,3.查看断点,查看断点使用info命令,常用的语法格式如下所示。,如例所示,查看断点2的信息,即代码第8行。,5.3.2,GDB,调试器使用,5.3,GDB,调试器,4.运行代码,断点设置完成后即可运行代码,输入r(run)开始运行(GDB默认从首行开始运行,如果需要从程序中指定的行开始运行,在r后添加行号即可)。如例所示,程序开始运行,到断点4(不包括第20行)停止。此时main函数中变量a、b,的值,分别为10、20。,5.3.2,GDB,调试器使用,5.3,GDB,调试器,如例所示,程序运行到例中所示的断点时,查看结果。变量a、b的值分别为10、20,子函数中变量temp显示无符号(此时子函数并未调用)。,5.查看变量值,使用命令p查看程序运行到断点时变量的值,其常用的语法格式如下所示。,5.3.2,GDB,调试器使用,5.3,GDB,调试器,6.恢复程序运行、单步运行,当程序在断点处停止时,使用命令continue可以恢复程序运行,一直到下一个断点处或程序结束。如例所示,继续运行代码,直到下一个断点处。,5.3.2,GDB,调试器使用,5.3,GDB,调试器,例,中,,程序调用子函数change,运行到断点处1。局部变量temp已经被赋值,形参a、b的值分别为10、20,实际参数a、b的值分别为10、20。如例所示,继续运行到断点2,可见形参的值发生变化,形参a的值变为20,形参b的值未变化为20。,5.3.2,GDB,调试器使用,5.3,GDB,调试器,如例所示,继续运行到断点3,此时子函数执行完成。查看变量的值,可见子函数中形参a、b的值确实发生了交换。,5.3.2,GDB,调试器使用,5.3,GDB,调试器,如例所示,继续运行到,断点,4,。查看变量的值,可见变量a、b的值未发生交换(a、b为实际参数)。子函数结束,变量temp的生命周期已经结束,因此显示无符号。,5.3.2,GDB,调试器使用,5.3,GDB,调试器,如例所示,继续运行到断点,5,。打印变量a、b的值,可见在执行交换后并未发生变化,这验证了形参的转变与实参没有任何关系。因此,例5-24的代码也是错误的,无法实现交换值的需求。,上述运行也可以使用命令step或,命令,next进行单步,操作。,单步执行的语法格式如下所示。,5.3.2,GDB,调试器使用,5.3,GDB,调试器,其中选项count可选择性设置,如果不添加count,每次执行一条指令;如果添加count,则代码一次执行count条指令,然后再停止。二者的本质区别为单步执行的指令数量不同。,命令step与next虽同为单步执行操作,但命令step在指定单步执行时,如果出现函数调用,则会进入该函数;命令next与,之,相反,在执行单步操作时,如果出现函数,调用,,不会进入该函数。,最后,给出,例5-24修改后的代码示例,读者可参考对比。,示例代码参考教材,5.3.2,节,,通过地址传递实现变量值的交换。,5.3.3,GDB,基本命令,5.3,GDB,调试器,5.3.2节通过一个完整的示例展示了如何使用GDB调试器对代码进行基本的调试。其中用到,了,一些GDB调试器的基本命令,这些命令,还,不能满足实际开发的测试需求。GDB作为一款强大的调试工具,支持的调试指令还有很多。,下面将,对这些命令进行说明,具体表,参考教材,5.3.3,节,。,GDB调试器的命令还有很多,包括对堆栈、变量、数组等操作,这里不再展示,读者可根据需求在网络中查询具体命令。,5.4 Make,工程管理器,5.4.1,Make,工程管理器简介,返回目录,5.4.2,Makefile,的使用,5.4.3,Makefile,的规则,5.4.1 Make,工程管理器简介,5.4,Make,工程管理器,Make是一种工程管理器,其本质为管理多文件的一种工具。通常情况下,Make被用来编译源代码,其特点在于Make可以实现自动编译,所谓自动编译即根据文件的时间戳自动发现更新过的文件,,以,减少编译的工作量。通俗地说,即Make在编译代码时,只编译改动的文件,其他未改动且已经编译过的文件将不再编译,这大大,缩短,了编译的时间,提高了编译的效率。,Make工程管理器的核心文件为Makefile。Makefile中有3个关键的元素:目标(Target)、依赖(Dependency)、命令(Command)。,5.4.1 Make,工程管理器简介,5.4,Make,工程管理器,Makefile解释了如何通过相关命令将依赖文件变为目标文件,而这些命令需传递给Shell去执行。因此,Makefile所使用的语言为脚本语言。,Makefile中最常用的语法格式如下所示。,其中“”表示通过Tab物理键进行缩进,注意,,,不能使用空格键代替。,5.4.2 Make,的使用,5.4,Make,工程管理器,Makefile的编写需要开发者具备一定的Shell编程能力,但是在实际的开发中,编写Makefile进行代码管理的情况比较少。一般情况下,开发者能调试Makefile代码,读懂较复杂的Makefile即可。Linux,操作,系统内核源代码中的Makefile编写十分经典,是众多嵌入式开发者学习并开发的模板。,下面,主要通过一个示例来展示Makefile的基本使用。,首先创建一个项目仓库(目录),并创建文件main.c、func.c、func.h,如例所示。,5.4.2 Make,的使用,5.4,Make,工程管理器,使用Vim编辑器编写文件内容,其中main.c文件内容如例所示,可,见其的,功能为调用封装函数func。,需要注意的是,使用include声明头文件,使用“”与“”表示的意义完全不同。使用引号时,先搜索工程文件所在目录,再搜索编译器头文件所在的目录;使用尖括号时则顺序相反。假设有两个文件名一样的头文件head.h,但内容不一样。,5.4.2 Make,的使用,5.4,Make,工程管理器,一个保存在编译器指定的头文件目录下,称为文件1;另一个则保存在当前工程的目录下,称为文件2。如果在指定头文件时使用的是“#include”,时引用的头文件是文件1。如果在指定头文件时使用的是“#include head.h”,时引用的头文件是文件2。,func.c文件的内容如例所示,可见代码func.c实现了函数func的功能。,5.4.2 Make,的使用,5.4,Make,工程管理器,main.c中指定了头文件func.h,其文件内容如例所示,用来声明函数func。这样编写的意思是,如果没有定义_FUNC_H,则定义_FUNC_H,并编译下面的代码,直到遇到#endif。这样,当重复引用时,由于_FUNC_H已经被定义,下面的代码将不会被编译,避免了重复定义。,5.4.2 Make,的使用,5.4,Make,工程管理器,综,合以,上示例代码,可知main.c中调用,了,函数func,func.c中实现了函数func的功能,func.h中声明了封装函数func。使用GCC编译器编译上述代码,如例所示。,5.4.2 Make,的使用,5.4,Make,工程管理器,例中,最终编译时,需要将实现子函数功能的代码与主函数所在的代码一同编译。如果封装的子函数较少,或者封装的文件较少,这种编译方式并没有太大的问题。但是,如果,程序调用的子函数较多,且这些子函数在不同的文件中实现,那么在执行编译命令时,则会显得十分笨拙。因此,通过Makefile实现多文件的编译管理,更加,方便。,运用,5.4.1节中介绍的Makefile基本语法格式,结合例的编译情况,编写一个基本的Makefile来实现代码的编译。,5.4.2 Make,的使用,5.4,Make,工程管理器,如例所示,。,例所示的Makefile,如果需要执行编译处理,则只需要执行make。,5.4.2 Make,的使用,5.4,Make,工程管理器,如例所示,。,如需要删除编译生成的目标文件,则执行make clean即可。执行此命令,意味着执行Makefile(例5-44)中clean关键字下的删除操作。,5.4.2 Make,的使用,5.4,Make,工程管理器,如例所示,执行删除操作。,例中展示的Makefile显然不是一个合理的版本,因为一旦编译,涉及,的文件名发生变化,则Makefile同样需要修改。这样的Makefile通用性并不高,因此可以考虑将Makefile变得更加通用化,这就需要使用Makefile中的自动变量。,5.4.2 Make,的使用,5.4,Make,工程管理器,Makefile中的变量与C语言程序中的变量,类似,,只是不指定变量的类型。而自动变量可以理解为Makefile中系统预定义的变量,无,须,用户定义,直接使用即可。,使用这些变量的目的就是替换原先文件中的名字或选项参数,从而减少Makefile,中需,修改的代码量,提高使用Makefile的工作效率。,5.4.2 Make,的使用,5.4,Make,工程管理器,Makefile中常用的预定义变量与自动变量如表所示。,5.4.2 Make,的使用,5.4,Make,工程管理器,利用表中给定的变量对例5-44中的Makefile进行修改。如例所示,使用自动变量替换原来的文件名。,5.4.2 Make,的使用,5.4,Make,工程管理器,使用例5-47所示的Makefile仍然不是最理想化的,。,可对Makefile添加自定义的变量,再次进行优化,如例所示。注意,Shell编程中引用变量需要使用“$”。,5.4.2 Make,的使用,5.4,Make,工程管理器,例所示的代码已基本符合Makefile的编写思想。,这样,,涉及修改Makefile中的文件时,只需要修改变量的赋值即可,明显减少了代码的修改量。,5.4.3 Make,的规则,5.4,Make,工程管理器,1.编译C程序的隐含规则,隐含规则:“xxx.o”的依赖会自动推导为“xxx.c”,并且其生成的命令为“$(CC)-c$(CPPFLAGS)$(CFLAGS)”。根据这一规则,再次对例的Makefile进行优化。如例所示,省略“.c”生成“.o”的执行代码,Makefile同样可以进行编译工作。,5.4.3 Make,的规则,5.4,Make,工程管理器,执行编译,如例所示。Makefile中使用“-Wall”选项执行严格编译,提示func.c中使用的puts函数未声明头文件。这里的提示可忽略,不影响执行。,2.链接目标文件的隐含规则,隐含规则:可执行文件“xxx”的依赖自动推导为“xxx.o”,如下所示。,5.4.3 Make,的规则,5.4,Make,工程管理器,由上述隐含规则可知,例的Makefile还可以继续优化,优化后的Makefile如例所示,读者可将其与例对比。需要注意的是,生成可执行文件的名字,必须是依赖的文件的名字之一。例如,生成的文件为main,依赖文件中有main.o,。,5.4.3 Make,的规则,5.4,Make,工程管理器,3.VPATH的使用,一些比较大的项目工程中存在大量的源代码。这种情况下,开发者通常的做法是将这些源文件分类,并存放在不同的目录中。因此,Makefile中应该在文件的前面添加路径,当make执行时,再,找寻文件的依赖关系。特殊变量VPATH用,于,完成该功能,如果没有指定该变量,make只会在当前目录下寻找依赖文件和目标文件;而如果指定该变量,make在当前目录,下,找不到,时,,,会,到指定的目录中寻找文件。,5.4.3 Make,的规则,5.4,Make,工程管理器,为了模拟上述情况,,下面,在5.4.2节展示的项目工程中,分别将项目文件放置到不同的目录下,如例所示。,5.4.3 Make,的规则,5.4,Make,工程管理器,此时,按照一般的编写规则,会对Makefile进行修改,如例所示。,5.4.3 Make,的规则,5.4,Make,工程管理器,不同于所有的项目源文件都在同一个目录的情况,,例中,的Makefile需要在指定依赖文件或目标文件时,指定这些文件的所在的路径。,如果Makefile使用VPATH变量,,,则无,须,为每一个目标或依赖文件设置路径,直接通过VPATH指定这些路径即可。当make执行时,,如果,在当前目录下未找到对应的文件,就会到VPATH指定的目录下寻找。由此,,可,结合,链,接目标文件的隐含规则,对例所示的代码,进行修改。,5.4.3 Make,的规则,5.4,Make,工程管理器,如例所示,。,本章主要介绍了Linux操作系统中4个常用的编程开发工具,分别是文本编辑器Vim、程序编译器GCC、调试器GDB、工程管理器,Make,。其具体的内容分为4个部分,:,Vim的安装、配置以及Vim的具体按键使用;GCC编译的流程、具体使用以及版本切换;GDB的使用以及相关命令;Makefile配置文件的编写。本章注重通过示例展示工具的使用,便于读者可以从实践的角度熟练掌握,其,用,法,。这些编程开发工具对于Linux操作系统而言十分重要。因此,作为一个合格的开发者,首先应该熟练掌握这些工具的使用,,然后,才能更好地进入开发的角色。,本章小结,
展开阅读全文