第5章 Linux编程基础.ppt_第1页
第5章 Linux编程基础.ppt_第2页
第5章 Linux编程基础.ppt_第3页
第5章 Linux编程基础.ppt_第4页
第5章 Linux编程基础.ppt_第5页
已阅读5页,还剩131页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第5章 Linux编程基础,Linux下的程序设计主要指C程序设计,它与其他环境中的C程序设计一样,主要涉及到编辑器、编译链接器、调试器及项目管理工具。,编辑器Linux中最常用的编辑器有Vi(Vim)和 Emacs,它们功能强大,使用方便,广受编程爱好者的喜爱。 编译链接器在Linux中,最常用的编译器是Gcc编译器。 调试器Gdb是绝大多数Linux 开发人员所使用的调试器,它可以方便地设置断点、单步跟踪等,足以满足开发人员的需要。 项目管理器Linux中的项目管理器“make”有些类似于Windows中Visual C+里的“工程”,它是一种控制编译或者重复编译软件的工具,另外,它还能自

2、动管理软件编译的内容、方式和时机,使程序员能够把精力集中在代码的编写上而不是在源代码的组织上。,5.1 文本编辑器vi,vi是visual interface的简称,类似于DOS中的Edit程序,vi是Linux系统的第一个全屏幕交互式编辑程序,利用它可以建立、修改文本文件。在编辑文本过程中,可以执行输出、删除、查找、替换及块操作等众多文本操作。,5.1.1 vi简介,Linux中有许多文本编辑工具,但由于vi编辑器是最常用、最标准的编辑器。 某些Linux版本中不提供其他编辑程序,但必须都提供vi这一基本的编辑程序。 对于所有版本的Linux系统,vi编辑器是完全相同的。,1vi的三种工作模

3、式,Vi编辑器的工作模式包括三种: 命令模式编辑器的普通操作,如移动光标、查找/替换等。 插入模式在编辑器内输入文本信息。 末行模式执行一些特殊命令 Vi编辑器的三种模式可相互切换。 在任何模式下,键入“Esc”键都会自动进入命令模式。 注意:改变vi的工作模式意味着改变键盘上按键的功能。,命令模式:,启动vi后首先就是处于命令模式,此时,所有从键盘的键入都被作为命令来接收,按下某键将执行特定的命令(类似于Windows下的快捷键),如移动光标、查找/替换命令等。 注意:vi编辑器的命令模式下所有的命令都是单字符的命令,插入模式:,在命令模式下,如果键入“i”,vi编辑器就进入插入模式,也称编

4、辑模式。 一旦进入插入模式,键盘上各个键的功能将改变,此时,键盘上的每个键代表一个将要键入到文本中的字符,键盘也就类似于一个打字机。 在插入模式下,键入“Esc”键,vi编辑器将自动回到命令模式,此时所有的按键又称为命令键。,在命令模式下,按“:”键即可进入末行模式。(此时,在屏幕的底行将出现一个编辑行,而光标将出现该行的行首。) 在末行模式下,可以在该行上键入字符串形式的末行命令。(例如:文件存盘退出,进入末行模式,最后键入字符“wq”并回车即可实现编辑程序文件的保存,同时退出vi编辑器。) 末行模式只是暂时性的模式,键入“Enter”键执行完末行命令后将自动退出该模式,并自动进入命令模式或

5、退出vi编辑器。,末行模式:,vi是Linux中一个功能强大的文本编辑程序,可以在启动vi时附带选项参数,以适应某些特殊的应用场合。 基本格式:vi 选项 文件名列表 常用选项说明: -r用于恢复系统突然崩溃时正在编辑的文件; -R用于以只读方式打开文件; +n用来指明进入vi编辑器后,屏幕显示到文件的第n行为止,如果不指定n,则显示到末行。 使用示例:(在shell命令行下,键入vi hello.c命令) xchvm-dev hello$ vi hello.c,2vi的运行与退出,如果当前目录下有hello.c文件,则打开该文件,如果当前目录下无该文件,则新建hello.c文件。此时进入的是

6、命令模式,光标位于屏幕的顶端。,在命令行模式下键入i 可切换Vi编辑器到插入模式,在屏幕底部显示有“插入”表示插入模式,在该模式下可以录入程序源代码。,在插入模式中,按下“Esc”建,则当前模式转入到命令模式,再键入“:”键,则从命令模式转入到末行模式,并在底行显示“:”,在末行模式下键入字符串命令“wq”(存盘退出),界面如图所示,未键入回车时,末行命令不被执行,键入回车可发现编辑器退出。,Vi编辑器的文本编辑命令包括移动光标、滚屏、删改、复制、粘贴等操作。 1基本的光标移动 在vi末行模式下,只能在末行输入末行命令,不能移动光标; 在vi插入模式下,可以使用键盘上的4个方向键移动光标,其他

7、字符键或组合键只能在光标所在位置插入一个新的字符; 在vi的命令模式下,可以使用4个方向键移动光标,也可以使用一些特殊键或组合键来整屏移动文本。,5.1.2 vi基本编辑命令,光标移动命令如下: 光标上移一行; 光标左移一个字符; 光标右移一个字符; 光标下移一行; ctrl+b 屏幕往“后”翻动一页,等价于PageDown键; ctrl+f 屏幕往“前”翻动一页,等价于PageUp键; ctrl+u 屏幕往“后”翻动半页; ctrl+d 屏幕往“前”翻动半页; 0(数字0)光标移至行首,等价于Home键; $光标移至行尾,等价于End键; G 光标移动到文件的最后; H光标移动到屏幕顶端第一

8、个非空格符处。,2vi命令模式下的基本命令,vi插入模式的基本命令只有一个,也就是键入“Esc”键退到命令模式。 键盘上的功能键与Windows环境下类似,如方向键、“PageUp”与“PageDown”键、“Insert”与“Delete”键、“Home”与“End”键等。 键入字符键或数字键相当于在光标位置新插入相应的文本。,3Vi插入模式下的基本命令,vi末行模式的基本命令主要包括保存文件、退出vi编辑器、显示行号等基本功能。具体命令如下:,说明:vi编辑器的命令较多,如多文件编辑、书签功能、块标记、选项配置等,请读者在熟悉vi的过程中慢慢体会。,4vi末行模式下的基本命令,5.2 GC

9、C编译器,GCC编译器能将C、C+语言源程序、汇编程序和目标程序编译、连接成可执行文件,如果没有给出可执行文件的名字,GCC将生成一个名为*.out的文件。 在Linux系统中,编译器通过程序的扩展名可分辨出编写原始程序代码所用的语言,由于不同程序所需要执行编译的步骤是不同的。因此GCC可根据不同的扩展名对它们进行分别处理,,GCC所支持的扩展名文件,5.2.1 GCC编译过程,在使用GCC编译程序时,整个编译过程可以被细分为四个阶段。分别为: 预处理(Pre-Processing); 编译(Compiling); 汇编(Assembling); 链接(Linking)。 使用GCC编译器的基

10、本语法格式如下: gcc 选项 准备编译的文件 选项 目标文件 说明:如果目标文件缺省,则自动生成的目标文件名与源程序文件名相同,扩展名为.out。,下面以一个具体的C程序实例来分析这四个阶段。文件名为hello.c,程序清单如下: 【清单5.1】 #include int main() printf(Hello,my dear friend!n); return(0); ,1预处理阶段 执行如下命令进行hello.c程序的预处理: rootvm-dev myprg#gcc -E hello.c -o hello.i 该命令使用选项“-E”指定只进行预处理,“hello.c”是源程序文件,选项

11、“-o”指定生成目标文件,“hello.i”是预处理过程生成的目标文件。因此,本次编译到预处理结束后就停止编译过程,但要把源代码中的stdio.h编译进来。 使用如下命令查看预处理过程生成的目标文件hello.i。 rootvm-dev myprg#vi hello.i,图5-4给出了观察到的hello.i文件末尾部分的截图情况,可以看出预处理过后,hello.i文件有915行,因为预处理过程把头文件stdio.h编译进来了。,2编译阶段 执行如下命令对预处理文件hello.i进行编译。 rootvm-dev myprg#gcc -S hello.i -o hello.S 该命令使用选项“-S

12、”指定只进行到编译阶段,“hello.i”是进行编译的源文件,选项“-o”指定生成目标文件,“hello.S”是编译生成的目标文件名。因此,本次编译到编译阶段结束后就停止编译过程,不进入汇编阶段。 使用如下命令查看编译过程生成的目标文件hello.S。 rootvm-dev myprg#vi hello.S,图5-5给出了观察到的hello.S文件的部分截图情况,可以看出编译阶段过后,GCC已经将hello.i文件转化为汇编文件。,3汇编阶段 执行如下命令对文件hello.S进行汇编。 rootvm-dev myprg#gcc -c hello.S -o hello.o 该命令使用选项“-c”

13、指定只进行到汇编阶段结束为止,“hello.S”是进行汇编的源文件,选项“-o”指定生成目标文件,“hello.o”是编译生成的目标文件名。 因此,本次编译到汇编阶段结束后就停止编译过程,不进入链接阶段。hello.o为二进制目标代码文件。,4链接阶段 在成功编译之后,就进入了链接阶段。在这里涉及到一个重要的概念:函数库。 在这个程序中并没有定义“printf”的函数实现,且在预编译中包含进的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,系统把这些函数实现都被做到名为libc.so.6的库文件中去了,在没有特别指定时,Gcc 会到系统默认的搜索路径“/usr/lib”下进行查找

14、,也就是链接到libc.so.6库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用。,函数库一般分为静态库和动态库两种。 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a”。 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时运行链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的libc.so.6就是动态库。GCC在编译时默认使用动态库。,完成了链接之后,GCC就可生成可执行文件,命令如下。 rootvm-dev myprg#

15、gcc hello.o -o hello 该命令gcc之后无选项参数,表示对指定的源文件进行编译,直到输出执行文件(示例中的源文件为hello.o,输出的执行文件hello)。 运行该可执行文件(执行命令./hello),出现正确的结果如下。 rootvm-dev myprg#./hello Hello,my dear friend!,5同时编译多个文件 在采用模块化的设计思想进行软件开发时,通常整个程序是由多个源文件组成的,相应的也就形成了多个编译单元,使用GCC能够很好地管理这些编译单元。 假设有一个由foo1.c和foo2.c两个源文件组成的程序,为了对它们进行编译,并最终生成可执行文件

16、foo,可以使用下面这条命令: rootvm-dev myprg#gcc foo1.c foo2.c -o foo,对于源文件不止一个情况,GCC编译过程仍然按照预处理、编译、汇编和链接的过程依次进行。因此,上面这条命令相当于依次执行如下三条命令。 rootvm-dev myprg#gcc -c foo1.c -o foo1.o rootvm-dev myprg#gcc -c foo2.c -o foo2.o rootvm-dev myprg#gcc foo1.o foo2.o -o foo,5.2.2 GCC编译选项介绍,1总体选项 GCC的常用总体选项如下,其中 “-E”、“-o”、“-S

17、”、“-c”选项在前一小节中已经介绍了其使用方法。后面只介绍另外两个非常常用的库依赖选项“-I dir”和“-L dir”。,1)-I dir选项 -I dir选项可以在头文件的搜索路径列表中添加dir目录。 Linux中的头文件的默认位置是“/usr/include/”目录,因此,当用户希望添加放置在其他位置的头文件时,就可以通过“-I dir”选项来指定,这样。 示例:在“/root/work/gcc/”目录下有两个文件hello1.c和my.h,而hello1.c 刚好添加了my.h头文件。,【清单5.2】 #include int main() printf(Hello!n); ret

18、urn 0; ,【清单5.3】 #include,这样,就可在GCC命令行中加入“-I”选项,使得编译过程在搜索头文件时包含指定的路径信息,GCC就能够执行出正确结果。 rootvm-dev Gcc#gcc hello1.c -I /root/work/gcc/ -o hello1 如果更改hello1.c的头文件引用方式,程序清单如下: #include “my.h” int main() printf(Hello!n); return 0; 可不在GCC命令行中加入“-I dir”选项,因为在include语句中,表示在标准路径中搜索,“”表示在本目录中搜索。,2)-L dir选项 选项“

19、-L dir”的功能与“-I dir”类似,能够在库文件的搜索路径列表中添加dir目录。例如有程序hello2.c需要用到目录“/root/work/gcc/lib/”下的一个动态库libxch.so,则只需键入如下命令即可: rootvm-dev Gcc#gcc hello2.c -L /root/work/gcc/lib/ hello2 注意:“-I dir”和“-L dir”都只是指定了路径,而没有指定文件,因此不能在路径中包含文件名。,2出错检查与警告提示选项 GCC包含完整的出错检查和警告提示功能,它们可以帮助Linux程序员写出更加专业和优美的代码。GCC的常用出错检查和警告提示选

20、项如下:,使用示例: C程序文件illcode.c,程序如清单5.4。 【清单5.4】 #include void main() long long var = 1; printf(This is not a standard C code!n); ,这段代码存在的问题: main函数的返回值被声明为void,但实际上应该是int; 使用了GNU语法扩展,即使用long long来声明64位整数,不符合ANSI/ISO C语言标准;,1)-ansi选项 该选项强制GCC生成标准语法所要求的告警信息,尽管这还并不能保证所有没有警告的程序都是符合ANSI C标准的。运行结果如下所示: rootvm

21、-dev myprg#gcc -ansi illcode.c -o illcode illcode.c:in function main: illcode.c:3:warning: return type of main is not int 从运行结果看,该选项并没有发现“long long”这个无效数据类型的错误,只发现了程序第3行的main函数返回类型错误。,2)-pedantic选项 该选项允许发出ANSI C标准所列的全部警告信息,同样也保证所有没有警告的程序都是符合ANSI C标准的。其运行结果如下所示: rootvm-dev myprg#gcc -pedantic illcode

22、.c -o illcode illcode.c:in function main: illcode.c:3:warning: return type of main is not int illcode.c:4:warning:ISO C90 does not support long long 从运行结果看,使用该选项查看出了“long long”这个无效数据类型的错误。,3)-Wall选项 该选项允许发出GCC能够提供的所有有用的告警信息。该选项的运行结果如下所示: rootvm-dev myprg#gcc -Wall illcode.c -o illcode illcode.c:3:wa

23、rning: return type of main is not int illcode.c:in function main: illcode.c:4: warning: unused variablr var 使用“-Wall”选项找出了未使用的变量var,但它并没有找出无效数据类型的错误。,3优化选项 GCC可以对代码进行优化,它通过编译选项“-On”来控制优化代码的生成,其中n是一个代表优化级别的整数。对于不同版本的GCC来讲,n的取值范围及其对应的优化效果可能并不完全相同,比较典型的范围是从0变化到2或3。 不同的优化级别对应不同的优化处理工作。如使用优化选项“-O”主要进行线程跳

24、转和延迟退栈两种优化。使用优化选项“-O2”除了完成所有“-O1”级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等。选项“-O3”则还包括循环展开和其他一些与处理器特性相关的优化工作。,使用示例: 源程序文件名为optimize.c,程序代码如清单5.5。 【清单5.5】 #include int main(void) double c; double result; double temp; for(c=0;c2000.0*2000.0*2000.0/20.0+2020;c+=(5-1)/4) temp = c/1979; result = c; printf(Result

25、 is %lfn, result); return(0); ,首先不加任何优化选项进行编译,并借助Linux提供的time命令统计出该程序在运行时所需要的时间。接下去使用优化选项来对代码进行优化处理,在同样的条件下再次测试一下运行时间,两次运行的时间对比情况如图5-6所示。,4体系结构相关选项 GCC的体系结构相关选项如下:,5.3 GDB调试器,一个功能强大的调试器不仅为程序员提供了跟踪程序执行的手段,而且还可以帮助程序员找到解决问题的方法。 对于Linux程序员来讲,GDB(GNU Debugger)通过与GCC的配合使用,为基于Linux的软件开发提供了一个完善的调试环境。 默认情况下,

26、GCC在编译时不会将调试符号插入到生成的二进制代码中,因为这样会增加可执行文件的大小。如果需要在编译时生成调试符号信息,可以使用GCC的-g或者-ggdb选项。,5.3.1 GDB使用流程,举例:程序文件名为test.c,程序代码如清单5.6。(通过对该程序的调试示例熟悉GDB的使用流程。),【清单5.6】 #include void sum(int m); int main() int i,n=0; sum(50); for(i=1; i=50; i+) n += i; ,printf(The sum of 1-50 is %d n, n ); return(0); void sum(int

27、 m) int i,n=0; for(i=1; i=m;i+) n += i; printf(The sum of 1-m is %dn, n); ,首先使用GCC对test.c进行编译。 注意:GDB进行调试的是可执行文件,而不是如“.c”的源代码,因此,需要先通过GCC编译生成可执行文件才能用GDB进行调试。编译时一定要加上选项“-g”,这样编译出的可执行代码中才包含调试信息,否则Gdb无法载入该可执行文件。 rootvm-dev myprg#gcc -g test.c -o test 说明:这段程序没有错误,但调试完全正确的程序可以更加了解GDB的使用流程。接下来就启动GDB进行调试。,

28、,,rootvm-dev myprg# gdb test GNU gdb Red Hat Linux (6.3.0.0-1.132.EL5rh) Copyright 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type show copying to see t

29、he conditions. There is absolutely no warranty for GDB. Type show warranty for details. This GDB was configured as i386-redhat-linux-gnu.Using host libthread_db library /lib/tls/libthread_db.so.1. (gdb),可以看出,在GDB的启动画面中指出了GDB的版本号、使用的库文件等信息,最后进入“(gdb)”命令行调试界面。,进入GDB调试环境,1查看程序源文件 在GDB调试器中,查看源程序文件的调试命令是

30、“list”,使用示例如下:,(gdb) list 1 #include 2 void sum(int m); 3 int main() 4 5 int i,n=0; 6 sum(50); 7 for(i=1; i=50; i+) 8 9 n += i; 10 ,(gdb) list 11 printf(The sum of 150 is %d n, n ); 12return(0); 13 14 void sum(int m) 15 16 int i,n=0; 17 for(i=1; i=m;i+) n += i; 18 printf(The sum of 1m is = %dn, n);

31、19 ,说明:缺省情况下,每次执行list命令只显示10行源程序文件,如果程序超过10行,则可继续使用list命令接着显示程序后面的代码。),2设置断点 设置断点是调试程序中的一个重要手段,它可以使程序执行到一定位置时暂停运行。程序员在该位置处可以方便地查看变量的值、堆栈情况等,从而找出代码的症结所在。 在GDB中设置断点非常简单,设置命令为“break n”,n表示断点所在的行号,具体使用示例如下: (gdb) break 6 Breakpoint 1 at 0 x8048384: file test.c, line 6. 注意:在GDB中利用行号设置断点是指代码运行到对应行之前将其停止,如

32、上例中,代码运行到第5行之后暂停(并没有运行第6 行)。,3查看断点设置情况 在设置完断点之后,用户可以键入“info b”来查看设置断点情况,在GDB中可以设置多个断点。 (gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0 x08048384 in main at test.c:6,4运行程序 接下来就可运行程序了,GDB默认从首行开始运行程序,运行程序的命令为“run”,若想从程序中指定行开始运行,可在run后面加上行号。 (gdb) run Starting program: /root/myprog/tes

33、t Reading symbols from shared object read from target memory.done. Loaded system supplied DSO at 0 x5fb000 Breakpoint 1, main () at test.c:6 6 sum(50); 注意:程序运行到断点处就停止了。,5查看变量值 在程序停止运行之后,程序员所要做的工作是查看断点处的相关变量值。在GDB中可查看变量的命令为“print 变量名”,使用示例如下: (gdb) print n $1 = 0 (gdb) print i $2 = 134518440 分析:为什么变量

34、“i”的值为如此奇怪的一个数字呢?而变量“n”的值却是正常的?,6单步运行 单步运行命令:“next”或“step”, “next”或“step”的区别:若有函数调用的时候,“step”会进入该函数而“next”不会进入该函数。 使用示例如下: (gdb)next The sum of 1-m is 1275 7 for(i=1; i=50; i+) (gdb) step sum (m=50) at test.c:16 16 int i,n=0; 可见,使用“next”后,程序显示函数sum的运行结果并向下执行,而使用“step”后则进入到sum函数之中单步运行。,7恢复程序运行 程序运行中断

35、后,可以使用命令“c”(continue)恢复程序的正常运行了。这时,它会把剩余还未执行的程序执行完,并显示剩余程序中的执行结果。以下是之前使用“next”命令恢复后的执行结果: (gdb) c Continuing. The sum of 1-50 is :1275 Program exited normally. 可以看出,程序在运行完后正常退出,之后处于“停止状态”。,5.3.2 GDB基本命令 GDB的命令可以通过查看help进行查找。 GDB的命令很多,因此GDB的help将其分成了很多种类。 各类如下所示: (gdb) help List of classes of command

36、s: aliases - Aliases of other commands breakpoints - Making program stop at certain points data - Examining data files - Specifying and examining files internals - Maintenance commands Type help followed by a class name for a list of commands in that class. Type help followed by command name for ful

37、l documentation. Command name abbreViations are allowed if unambiguous.,上述列出了GDB 各个分类的命令,倒数第三行说明其为分类命令。可以具体查找各分类种的命令。数据相关所示: (gdb) help data Examining data. List of commands: call - Call a function in the program delete display - Cancel some expressions to be displayed when program stops delete mem

38、- Delete memory region disable display - Disable some expressions to be displayed when program stops Type help followed by command name for full documentation. Command name abbreViations are allowed if unambiguous.,函数调用相关命令如下: (gdb) help call Call a function in the program. The argument is the funct

39、ion name and arguments, in the notation of the current working language. The result is printed and saved in the value history, if it is not void.,GDB中的命令主要分为以下几类: 工作环境相关命令; 设置断点与恢复命令; 源代码查看命令; 查看运行数据相关命令及修改运行参数命令。,1工作环境相关命令 GDB中不仅可以调试所运行的程序,而且还可以对程序相关的工作环境进行相应的设定,甚至还可以使用shell中的命令进行相关的操作,其功能极其强大。常见工作

40、环境相关命令如下所示:,2断点设置与恢复命令 GDB 中设置断点与恢复的常见命令如下:,GDB中设置断点有多种方式:如按行设置断点、按函数设置断点、按条件设置断点。按行设置断点的方法已经在前文介绍,这里就不重复了。下面结合前一节的代码,具体介绍后两种设置断点的方法。,1)函数断点,GDB中按函数设置断点只需把函数名放在命令“break”之后,如下所示: (gdb) break sum Breakpoint 1 at 0 x80483d1: file test.c, line 16. (gdb) info b Num Type Disp Enb Address What 1 breakpoint

41、 keep y 0 x08048d1 in sum at test.c:16,2)条件断点 GDB中设置条件断点的格式为:break行数或函数名if表达式。具体实例如下所示: (gdb) break 8 if i= =10 Breakpoint 1 at 0 x80483a3: file test.c, line 8. (gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0 x080483a3 in main at test.c:8 stop only if i = = 10 (gdb) run Starting pr

42、ogram: /root/myprog/test The sum of 1-m is 1275 Breakpoint 1, main () at test.c:9 9 n += i; (gdb) print i $1 = 10,3GDB中源码查看相关命令 在GDB中可以查看源码以方便其他操作,它的常见相关命令如下:,4GDB中查看运行数据相关命令 GDB中查看运行数据是指当程序处于“运行”或“暂停”状态时,可以查看的变量及表达式的信息,其常见命令如下:,5GDB中修改运行参数相关命令 GDB还可以修改运行时的参数,并使该变量按照用户当前输入的值继续运行。它的设置方法为:在单步执行的过程中,键入

43、命令“set 变量设定值”。这样,在此之后,程序就会按照该设定的值运行。结合上一节的代码将n的初始值设为4,示例如下所示:,(gdb) break 7 Breakpoint 5 at 0 x8048391: file test.c, line 7. (gdb) run Starting program: /root/myprog/test The sum of 1-m is 1275 Breakpoint 5, main () at test.c:7 7 for(i=1; i=50; i+) (Gdb) set n=4 (Gdb) c Continuing. The sum of 1-50 i

44、s 1279 Program exited normally. 可以看到,最后的运行结果确实比之前的值大。,5.4 make工程管理器,所谓工程管理器,顾名思义,是指管理较多的文件的。 编译过程分为预处理、编译、汇编、链接4个阶段,其中编译阶段仅检查语法错误以及函数与变量的声明是否正确, 在链接阶段则主要完成函数链接和全局变量的链接。 因此,那些没有改动的源代码根本不需要重新编译,而只要把它们重新链接进去就可以了。所以,人们就希望有一个工程管理器能够自动识别更新了的文件代码,同时又不需要重复输入冗长的命令行,这样,make工程管理器也就应运而生了。 实际上,make工程管理器也就是个“自动编译

45、管理器”,这里的“自动”是指它能够根据文件时间戳自动发现更新过的文件而减少编译的工作量。,5.4.1 Makefile文件格式,Makefile是一个文本形式的数据库文件,就像一组shell语句,其中包含一些规则,它告诉make编译哪些文件、怎样编译以及在什么条件下去编译,甚至于进行更复杂的功能操作。make在执行时就会找到Makefile文件,自动根据修改情况完成源文件对应的.o文件的更新、库文件的更新和最终可执行文件的更新。在源文件无更改的情况下,make什么也不做。,Makefile文件格式,既然make工程管理器的一切行为都依据Makefile文件,那么编写好Makefile是至关重要

46、的事情。一个简单的Makefile文件的语法格式如下: target : dependency dependency COMMAND COMMAND ,target:目标体。即make最终需要创建的对象。另外,目标也可以是一个make执行的动作名称,后面有实例介绍其使用方法。 dependency:依赖体。依赖体通常是编译目标体要依赖的一个或多个文件。 command:命令。为了从指定的依赖体创建出目标体所需执行的shell命令。一个规则可以有多个命令行,每一条命令占一行。 注意:每一个命令的第一个字符必须是制表符Tab,如果使用空格会产生问题,make会在执行过程中显示Missing Sep

47、arator(缺少分隔符)并停止。,Makefile文件各字段的解释,假设:/home/xch/myprg/目录下有两个文件分别为hello.c和hello.h,文件hello.h在文件hello.c中被引用,要创建的目标文件为hello。 分析: 编译过程的target应该是hello; 编译过程的dependency应该是文件hello.c和hello.h; 编译过程从依赖体创建目标体所执行的COMMAND命令应该是编译指令(gcc -c hello.c -o hello)。,Makefile使用示例,制作Makefile文件 打开vi编辑器编写Makefile文件。 rootvm-dev

48、 myprg#vi Makefile 对应的Makefile文件内容写为: hello: hello.c hello.h gcc hello.c -o hello clean: rm -fr hello.c hello.h,Makefile使用示例(续),使用make命令 命令格式如下: make target 执行make命令时,自动读入Makefile文件并执行对应target的COMMAND语句,并找到相应的依赖文件。示例如下: rootvm-dev myprg#make hello gcc hello.c -o hello rootvm-dev myprg#ls hello hello

49、.c hello.h Makefile rootvm-dev myprg#make clean rm -fr hello.c hello.h rootvm-dev myprg#ls hello Makefile,上述示例中 make hello命令执行了目标体(hello)所对应的命令语句(gcc hello.c -o hello),并生成了该目标体(hello)。 make clean命令则执行相应命令(rm -fr hello.c hello.h)删除了文件hello.c和hello.h。 注意:执行make命令时自动检测相关文件的时间戳。如执行make hello命令,make首先检查目

50、标体和依赖体之间的时间戳,如果hello.c或hello.h的时间戳比hello更新,则对应的COMMAND命令被执行,否则不被执行。,5.4.2 Makefile文件组成,在实际中使用的Makefile往往是包含很多的文件和命令,通常由五部分组成,分别是伪目标、变量、隐式规则、模式规则和注释。,1Makefile伪目标,除了一般的文件目标体(如上例的hello)外,make也允许指定伪目标(并不依赖实际的文件,如上例的clean,编译完成后,不会产生clean这个文件)。 伪目标规定了make应该执行的命令,通常没有依赖体。 在实际开发中,这个伪目标clean非常有用,比如开发人员希望进行一

51、次彻底重新编译,就必须删除原编译过程的所有记录,让make认为所有依赖体和目标体都是新的。,1)执行make命令,当遇到目标体clean时,make先查看其是否有依赖体。因为clean没有依赖体,所以make认为目标体是最新(没有任何更改发生)而不执行任何操作。 2)执行make clean时,首先检查当前目录下是否有clean目标文件存在,如果不存在名为clean的文件,那么make就要执行目标体clean对应的COMMAND命令“希望生成”该文件(实际并没有生成该目标体)。 3)如果恰巧当前目录有一个名为clean的文件存在,执行make clean时就会发现它,而执行make clean

52、命令并不会真的生成clean文件,对于make而言,这个clean文件一定是最新的,使得目标体clean所对应的COMMAND命令也不被执行。,Makefile包含伪目标体时,Make的工作方式,make clean使用示例,2Makefile变量 make允许在Makefile中创建和使用变量。所谓的变量就是在Makefile文件中为文本串定义的一个名字(相当于C语言中的字符串常量),这个变量的值即文本串内容。 变量的定义示例: var_name = Hello,my friend 变量名为var_name,变量的值为hello_my_friend。 变量的引用: 把变量用括号括起来,并在前

53、面加上“$”符号,就可以引用该变量的值,如$(var_name)就代表Hello,my friend。 (var_name即是上面定义的变量),OBJS = howdy.o helper.o HDRS = helper.h howdy: $(OBJS) $(HDRS) gcc $(OBJS) -o howdy helper.o: helper.c $(HDRS) gcc -c helper.c -o helper.o howdy.o: hello.c gcc -c hello.c -o howdy.o clean: rm -fr howdy hello*.o,Makefile变量使用示例,说明

54、:变量一般都在makefile的头部定义,它起的功能是替换。 执行make命令时,OBJS和HDRS在被引用的每个地方被展开。,make使用的两种变量 简单展开变量:只在变量使用处直接展开,示例如下: TOPDIR = /home/C/makefile/ SRCDIR = $(TOPDIR)src 递归展开变量:在引用时逐层展开,即如果在展开式中包含了对其他变量的引用,则这些变量也将被展开,直到没有需要展开的变量为止。示例如下: CC = gcc CC = $(CC) o 分析:上例的递归展开使用存在什么问题?,Makefile其他变量,除前面介绍的用户自定义变量(如OBJS、HDRS)外,m

55、ake也允许使用环境变量、自动变量和预定义变量。 环境变量工作环境的相关变量,如路径、时间等。 自动变量用于代表编译语句中出现的目标文件和依赖文件,并且具有本地含义。 预定义变量通常指编译器、汇编器的名称及其编译选项。,预定义变量,自动变量,自动变量使用示例,OBJS = howdy.o helper.o HDRS = helper.h howdy: $(OBJS) $(HDRS) gcc $ -o $ helper.o: helper.c $(HDRS) gcc -c $ -o $ howdy.o: hello.c gcc -c $ -o $ clean: rm -fr howdy hell

56、o*.o,3Makefile规则,Makefile的规则是Make进行处理的依据,它包括了目标体、依赖文件及其之间的命令语句。一般情况下,Makefile中的一条语句就是一个规则。在上面的例子中,都显式地指出了Makefile中的规则关系,如“rm -fr howdy hello*.o”。 为了简化Makefile的编写,make还定义了隐式规则和模式规则,,1)隐式规则,隐含规则能够告诉make怎样使用传统的技术完成任务,当用户使用它们时就不必详细指定编译的具体细节,而只需把目标文件列出即可。Make会自动搜索隐式规则目录来确定如何生成目标文件。 隐式规则使用示例: OBJS = edito

57、r.o screen.o keyboard.o editor : $(OBJS) gcc -c $(OBJS) -o $ .PHONY : clean clean : rm editor $(OBJS),对每一个名为.o的目标文件,make首先找到与之相应的源代码文件,并且用gcc -c xxx.c -o xxx.o编译生成这个目标文件。所以本例中make先查找名为editor.c、screen.c和keyboard.c的文件并将它们编译为目标文件editor.o、screen.o和keyboard.o,最后生成默认目标体editor,这就是隐式规则的功能。,常见隐式规则,2)模式规则 模式规

58、则是用来定义相同处理规则的多个文件的。它不同于隐式规则,隐式规则仅仅能够用make默认的变量来进行操作,而模式规则还能引入用户自定义变量,为多个文件建立相同的规则,从而简化Makefile的编写。 模式规则的格式类似于普通规则,这个规则中的相关文件前必须用“%”标明。,OBJS = hallo.o helper.o howdy: $(OBJS) gcc $(OBJS) -o howdy helper.o: helper.c gcc -c helper.c -o helper.o hallo.o: hello.c gcc -c hello.c -o hallo.o clean: rm -fr h

59、owdy hallo.o,OBJS = hallo.o helper.o howdy: $(OBJS) gcc $(OBJS) -o howdy %.o: %.c gcc -c %.c -o %.o clean: rm -fr howdy hallo.o,模式规则使用示例,模式规则的格式类似于普通规则,这个规则中的相关文件前必须用“%”标明。下例是使用模式规则可把左边的Makefile修改为右边的Makefile文件。,4Makefile注释,makefile文件中只有行注释,其注释用“#”字符,就像C语言中的“/”,如果要在makefile文件中使用“#”字符,可以使用“”进行转义,即“#”。,使用Make管理器非常简单,只需在make命令的后面键入目标名即可建立指定的目标,如果直接运行make,则建立Makefile文件中的第一个目标体。,5.4.3 Make管理器选项,常用的make命令行选项,对于工程较大的系统设计时,Makefile文件的制作非常困难,也特别耗时。 autoTools 是系列工具,它包含了aclocal、

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论