Linux下的编程工具有哪些?

Linux下的编程工具有哪些?

BBS水木清华站∶精华区

发信人:Kongming(没日没夜...),信区:Linux
标题:Re:如何学习linux下编程?
发信站:BBS水木清华站(MonAug211:27:041999)

我为你贴一篇文章吧,简要介绍了Linux下的编程工具,
入门是没有问题的。

这是我最近编写的书的部分内容。

严正声明:
本文由Kongming网友享有专有版权。
版权所有,未经许可,不准随意复制、改编或变相改编、扩散。

主要包含如下内容:
?GNUC、C++编译器
?GNUmake工具
?GNU的调试器
?RCS版本控制系统
并简单介绍了Linux系统上广为流行的Perl脚本语言。
对比了Linux和WindowsNT两种不同的编码风格。

在BBS上看有些困难,拿回家慢慢看吧。



第十三章软件开发
由于操作系统只是一个工具,对大多数读者来说,学习操作系统的最终目的是利用操
作系统上的应用软件完成自己的工作,或者利用开发工具开发应用程序。因此,对一个操
作系统来说,可获得的开发工具,以及这些开发工具是否有效,就成为评价操作系统的一
个重要因素。
这一部分的后三章将主要从开发人员的角度比较Linux和WindowsNT。本章的主要
内容如下:
?介绍操作系统为开发人员提供的各种系统服务;
?介绍操作系统上的主要开发工具。
考虑到许多读者可能对Linux上的开发工具了解不多,因此,本章将介绍一些Linux
上常用的开发工具。当然,软件开发并不是区区一章就能够讲得清楚的,更多的需要读者
自己去实践。
13.1POSIX标准和操作系统接口
13.1.1Linux和WindowsNT对POSIX标准的支持
从第一章的内容我们知道,POSIX是一种标准,主要定义了操作系统的系统调用接口,
POSIX标准最初为提高UNIX系统之间的移植性而开发。Linux是一种POSIX兼容系统,
而WindowsNT中包含了POSIX子系统。但这两个操作系统的POSIX之间有什么区别呢?
POSIX是由非常复杂的标准体系组成的,其中广为接受的POSIX标准是POSIX.1标
准。POSIX.1是一个源代码级的兼容性标准,提供了操作系统的C语言编程接口,内容
涉及到POSIX.1a(系统接口扩展),POSIX.1b(实时),POSIX.1c(线程),POSIX.1d(实
时扩展),POSIX.1e(安全性),POSIX.1f(透明文件访问),POSIX.1g(协议无关服务)
以及POSIX.1h(容错)等。POSIX的另外一个重要标准是POSIX.2标准,它定义了操作
系统的Shell和工具标准。通常谈到的POSIX标准就是这两个标准。Linux支持完整的
POSIX.1和POSIX.2标准;而WindowsNT只提供对POSIX.1的支持,而且是一个不完
整的系统,许多方面(例如I/O)需要Win32API来完成。实际上,WindowsNT的许多
POSIX.1接口是通过Win32API间接实现的。
WindowsNT中的POSIX.1版本基本上没有什么用处,在VisualStudio6.0中,
甚至取消了对POSIX.1的支持。如果读者要在WindowsNT上进行一些POSIX的开发工
作,建议不要使用WindowsNTPOSXI.1。用户可以使用第三方POSIX系统,或者由GNU提
供的GNUWin32工具。这些产品提供了更强大的功能,例如SoftwareSystemInc.的
POSIX.2工具箱(OpenNT)具有如下特征:真正的UNIXShell,以及在WindowsNT中
运行XWindow的能力等。这些软件为WindowsNT的非完整POSIX.1系统增加了如下
特性:
?POSIX.1、POSIX.2和ANSIC接口;
?可在Intel和Alpha平台上使用;
?BSD套接字支持;
?SystemV的共享内存和信号灯IPC机制;
?X11R5客户、库和头文件;
?OPENNTIF(OSF/Motif1.2.4窗口管理器和开发库);
?Colorcurses库;
?完整的Shell作业管理;
?磁带设备支持;
?UNIX开发工具:make、rcs、yacc、lex、cc、c89、nm、ar、strip等。
GNUWin32是针对x86和PowerPC的WindowsNT/95的GNU开发工具。使用这些
开发工具开发的应用程序能够访问MicrosoftWin32API,也能访问Cygwin32API,它
提供了附加的类似UNIX的功能,包括UNIX套接字和进程控制等。利用这些工具,可增
强WindowsNT上POSIX子系统:
?利用标准的MicrosoftWin32API或/和Cygwin32API编写Win32控制台或
GUI应用程序;
?能够方便地从源代码中配置和建立许多GNU工具,包括GNUWin32开发工具
本身;
?可将许多重要的UNIX工具移植到WindowsNT而不用对源代码进行大规模修
改;
?包含有相当完整的UNIX环境工具,可使用许多普通的UNIX系统工具。
有关上述这两种软件,读者可访问如下站点:
http://www.softway.com/OpenNT/homet.htm
htt://www.cygus.com/misc/gnu.win32
和WindowsNT相反的是,Linux是一个POSIX.1标准的完全兼容系统。Linux上
的POSIX.2标准实现是由GNU工具和函数库提供的,其中最主要的C函数库是glibc。
该函数库包含如下内容:
?ISOC:C编程语言的国际标准。GNUC函数库与美国国家标准局(ANSI)公布
的C标准兼容。这一标准后来被国际标准化组织所接收(ISO/IEC9899:1990)。
?POSIX:操作系统的ISO/IEC9945(即IEEE1003)标准。GNUC函数库实现了
ISO/IEC9945-1:1996(POSIX系统应用程序编程接口,即POSIX.1)指定的
所有函数。该标准是对ISOC的扩展,包括文件系统接口原语、设备相关的终
端控制函数以及进程控制函数。同时,GUNC函数库还支持部分由ISO/IEC
9945-2:1993(POSIXShell和工具标准,即POSIX.2)指定的函数,其中包
括用于处理正则表达式和模式匹配的函数。
?BerkeleyUnix:BSD和SunOS。GNUC函数库定义了某些UNIX版本中尚未标
准化的函数,尤其是4.2BSD,4.3BSD,4.4BSDUnix系统(即“BerkeleyUnix”)
以及“SunOS”(大众化的4.2BSD变种,其中包含有某些UnixSystemV的功
能)。BSD函数包括符号链接、select函数、BSD信号处理函数以及套接字等
等。
?SVID:SystemV的接口描述。SystemV接口描述(SVID)是描述AT&TUnixSystem
V操作系统的文档,它是对POSIX标准的扩展超集。GNUC函数库定义了大多
数由SVID指定而未被ISOC和POSIX标准指定的函数。来自SystemV的
支持函数包括进程间通信和共享内存、hsearch和drand48函数族、fmtmsg以
及一些数学函数。
?XPG:X/Open可移植性指南。X/Open可移植性指南(由X/OpenCompany,Ltd.
出版),是比POSIX更为一般的标准。X/Open拥有Unix的版权,而XPG则
指定成为Unix操作系统必须满足的需求。GNUC函数库遵循X/Open可移植
性指南(Issue4.2)以及所有的XSI(X/Open系统接口)兼容系统的扩展,
同时也遵循所有的X/OpenUnix扩展。
同时,Linux也包含有许多字符界面管理、网络协议处理以及图形图象处理的函数库,
这些函数库均作为一般Linux商业发行版的一部分而一同发行。有关这些函数库的详细
介绍,读者可参阅附录C。
实际上,Microsoft并没有打算自己的操作系统做到与UNIX完全兼容,如果读者看
到过由微软(中国)有限公司编著的《MicrosoftSQLServer6.5技术参考》(科学出版
社、龙门书局1997年出版)一书,就可以明白微软和UNIX阵营的激烈竞争。既然
Microsoft在该书中说UNIX如何如何不好,当然就不会对来自UNIX的POSIX有什么
好的支持了。
那么,WindowsNT的操作系统接口包括哪些内容呢?
13.1.2WindowsNT的操作系统接口
WindowsNT的操作系统服务主要体现在Win32API上,大体可分为如下几类:
?窗口管理。应用程序可用来创建和管理窗口的部分,涉及到窗口、消息、消息
队列、控件、对话框、鼠标和键盘输入、定时器、菜单、键盘加速键以及其他
与窗口管理有关的内容。
?图形设备接口。应用程序用来实现设备无关图形的部分,涉及到设备描述表、
坐标转换、元文件、位图、图标、画刷、画笔、区域、直线和曲线、字体、颜
色、路径、剪切、打印等。
?系统服务。应用程序用来实现基本多任务管理的Win32API部分,涉及到进程
和线程、内存管理、网络、动态链接、安全性以及其他有关系统的内容。
?多媒体服务。Win32API中多媒体支持,涉及到音频、视频、媒体控制、多媒
体文件输入和输出以及增强的定时器功能等。
?扩展库。为Win32API额外增加的库,涉及到公用对话框、简化动态数据交换
(DDE)的管理函数、Shell中增强的拖放操作、文件安装函数以及数据解压缩
函数。
可明显看出,POSIX和Win32API是两个完全不同的操作系统接口体系。如果仅比较
这两个操作系统为用户提供的接口及函数库的话,可明显看到有如下特点:
?基于POSIX的操作系统接口部分比Win32的系统服务部分要简洁得多。POSIX的
接口函数大概只有二百多个;而Win32的系统服务部分的函数约有上千个,而
且调用接口相对复杂。
?Linux上除操作系统接口之外的函数库非常丰富。笔者曾经在MS-DOS、Windows
3.1、Windows95/NT下进行过C/C++的开发工作,当我接触到Linux时,发
现许多原来需要自己编写程序实现的功能,Linux上已经有的可用的函数库。
13.2VisualStudio和GNU
在WindowsNT上进行开发,用户主要使用的通用开发工具有:
?MicrosoftVisualStudio。该开发工具包是Microsoft最完整的开发工具包,
也是使用最为广泛的开发工具包,其中包含有VisualC++、VisualBasic、Visual
FoxPro等开发工具。这些工具一般是集成开发环境,利用这些工具,可在一个
程序中完成编辑、编译、调试等各项工作,对提高开发效率有帮助。
?BorlandC++。该工具是Borland公司的C/C++编译器,和VisualC++类似,
也是一个集成环境。
?BorlandDelphi。该工具是Borland公司开发的Pascal编译器,Borland公
司为了和VisualBasic竞争而开发,拥有较为广泛的用户群,也是一个集成
环境。
WindowsNT上的开发工具都有一个特点,它们均是一些集成开发工具。作为一个程
序员,笔者对集成开发环境是否能够真正提高开发效率,或者能够在多大程度上提高开发
效率保持怀疑态度。因为许多程序员并不使用集成的开发环境,但也同样富有效率。Linux
内核开发小组就是一个例子,在开发操作系统的时候,是无法使用集成开发工具的。使用
集成开发环境有一个缺点,就是容易让程序员养成懒惰的习惯。笔者就更加愿意在程序出
问题的时候去认真地分析程序,而不是一味依赖调试工具。
和WindowsNT上的集成开发环境不同,Linux上的开发工具是一些零散的GNU工
具。例如,程序员可能使用VIM编写程序,然后用make或gcc编译程序,如果程序有
错误,用gdb调试程序等。当然,也有一些集成的开发环境可以使用,例如xwpe(Window
ProgrammingEnvironment)就是一个集成开发环境。如果使用Emacs,程序员也可以借
助Emacs的强大配置能力为自己定制一个集成的开发环境。
Linux下的程序开发还有一个特点,就是可以借助许多脚本语言构造复杂的程序。例
如,程序员可借助Shell脚本编写处理文件、目录的脚本程序,也可以利用Perl
(PracticalExtractionReportLanguage)编写用来处理文本文件和生成报表的脚本程
序,还可以利用Tcl/Tk的脚本程序编写图形用户界面。如果恰当地使用这些脚本语言,
可以达到事半功倍的效果。在WindowsNT下,能够被称为脚本语言的大概算是VBA(Visual
BasicforApplication),但VBA仅在有限的应用软件中支持,例如,MicrosoftOffice,
而并不被操作系统支持。现在,Microsoft正打算将Perl的支持添加到它的操作系统中。
本章将简单介绍一些Linux下常见的开发工具以及有关Perl脚本编程的知识。由
于Tcl/Tk主要是用来编写图形用户界面的,我们将在下一章讲解Tcl/Tk的使用。
13.3Linux上的开发工具
13.3.1获取帮助
在Linux上,程序员获取帮助的最常用工具是man或xman。利用man或xman,
程序员可以查看特定函数的说明。例如,键入manprintf将显示printf函数的详细调
用接口。由于Linux上大部分软件开发工具都来自自由软件基金会的GNU项目,因此,
程序员可以使用另外一个程序获得有关开发工具使用的详细帮助信息,这一程序就是
info,它是GNU的超文本帮助系统。程序员可在命令行键入info进入info帮助系统,
也可在Emacs中键入Esc-x,info(或C-h,i)而进入info帮助系统。
在info中,在线帮助文本被组织成一个个节点(node),每个节点代表一个特定的
主题。屏幕上的第一行显示了该节点的标题,如图13-1所示。
(屏幕图)
图13-1在Emacs中访问info帮助系统
info帮助系统的初始屏幕显示了一个主题目录,你可以将光标移动到带有*的主题
菜单上面,然后按回车键进入该主题,也可以键入m,后跟主题菜单的名称而进入该主题。
例如,你可以键入m,然后再键入gcc而进入gcc主题中。
如果你要在主题之间跳转,则必须记住如下的几个命令键:
?n:跳转到该节点的下一个节点;
?p:跳转到该节点的上一个节点;
?m:指定菜单名而选择另外一个节点;
?f:进入交叉引用主题;
?l:进入该窗口中的最后一个节点;
?TAB:跳转到该窗口的下一个超文本链接;
?RET:进入光标处的超文本链接;
?u:转到上一级主题;
?d:回到info的初始节点目录;
?h:调出info教程;
?q:退出info。
13.3.2GNUC和C++编译器
Linux中最重要的软件开发工具是GCC。GCC是GNU的C和C++编译器。实际上,
GCC能够编译三种语言:C、C++和ObjectC(C语言的一种面向对象扩展)。利用gcc命
令可同时编译并连接C和C++源程序。
13.3.2.1GCC基本使用
利用GCC编译并连接少数几个C源文件是简单的。假设读者的一个程序只有一个源
文件hello.c,其内容如下:
#include<stdio.h>
#include<stdlib.h>

intmain()
{
printf("Hello,world!/n");

return0;
}
则只需在命令键入gcc-ohellohello.c就可以编译、连接并生成一个可执行文件
hello:
[WeiYM@versagcc]$gcc-ohellohello.c
[WeiYM@versagcc]$./hello
Hello,world!
如果你有两个或少数几个C源文件,也可以方便地利用GCC编译、连接并生成可执
行文件。例如,假设你有两个源文件main.c和factorial.c两个源文件,现在要编译
生成一个计算阶乘的程序。这两个源文件的内容如清单13-1和清单13-2所示。
清单13-1factorial.c
―――――――――――――――――――――――――――――――――――――――
#include<stdio.h>
#include<stdlib.h>

intfactorial(intn)
{
if(n<=1)
return1;

else
returnfactorial(n-1)*n;
}
―――――――――――――――――――――――――――――――――――――――
清单13-2main.c
―――――――――――――――――――――――――――――――――――――――
#include<stdio.h>
#include<stdlib.h>


intfactorial(intn);

intmain(intargc,char**argv)
{
intn;

if(argc<2){
printf("Usage:%sn/n",argv[0]);
return-1;
}
else{
n=atoi(argv[1]);
printf("Factorialof%dis%d./n",n,factorial(n));
}

return0;
}
―――――――――――――――――――――――――――――――――――――――
利用如下的命令可编译生成可执行文件,并执行程序:
[WeiYM@versagcc]$gcc-ofactorialmain.cfactorial.c
[WeiYM@versagcc]$./factorial5
Factorialof5is120.
在上面的GCC命令选项中,使用了一个-o选项,该选项指定了编译/连接生成的输
出文件的名称。如果不指定该输出文件的名称,则GCC自动建立一个a.out文件。
我们也可以分别对上述两个文件进行编译,然后再连接起来,这时可使用GCC的-c选
项,如下所示:
[WeiYM@versagcc]$gcc-cmain.c
[WeiYM@versagcc]$gcc-cfactorial.c
[WeiYM@versagcc]$gcc-ofactorialmain.ofactorial.o
[WeiYM@versagcc]$./factorial10
Factorialof10is3628800.
13.3.2.2用GCC编译C++程序
GCC可同时用来编译C程序和C++程序。一般来说,C编译器通过源文件的后缀名
来判断是C程序还是C++程序。在Linux中,C源文件的后缀名为.c,而C++源文
件的后缀名为.C或.cpp。
但是,gcc命令只能编译C++源文件,而不能自动和C++程序使用的库连接。因此,
通常使用g++命令来完成C++程序的编译和连接,该程序会自动调用gcc实现编译。
假设我们有一个如下的C++源文件(hello.C):
#include<iostream.h>

voidmain(void)
{
cout<<"Hello,world!"<<endl;
}
则可以如下调用g++命令编译、连接并生成可执行文件:
[WeiYM@versagcc]$g++-ohellohello.c
[WeiYM@versagcc]$./hello
Hello,world!
13.3.2.3GCC的其他选项
gcc命令的基本语法是:
gccoptionsfilename
gcc的选项一般以减号(-)开头,有短的选项,例如上面看到的-o或-c,也有长
的选项。表13-1给出了gcc命令的常用选项。
表13-1gcc命令的常用选项
选项
解释
-ansi
只支持ANSI标准的C语法。这一选项将禁止GNUC的某些特色,
例如asm或typeof关键词。
-c
只编译并生成目标文件。
-DMACRO
以字符串“1”定义MACRO宏。
-DMACRO=DEFN
以字符串“DEFN”定义MACRO宏。
-E
只运行C预编译器。
-g
生成调试信息。GNU调试器可利用该信息。
-IDIRECTORY
指定额外的头文件搜索路径DIRECTORY。
-LDIRECTORY
指定额外的函数库搜索路径DIRECTORY。
-lLIBRARY
连接时搜索指定的函数库LIBRARY。
-m486
针对486进行代码优化。
-oFILE
生成指定的输出文件。用在生成可执行文件时。
-O0
不进行优化处理。
-O或-O1
优化生成代码。
-O2
进一步优化。
-O3
比-O2更进一步优化,包括inline函数。
-shared
生成共享目标文件。通常用在建立共享库时。
-static
禁止使用共享连接。
-UMACRO
取消对MACRO宏的定义。
-w
不生成任何警告信息。
-Wall
生成所有警告信息。

gcc的其他例子,可参阅info帮助或gcc手册页。作为例子,请看如下的gcc命
令:
$gcc-otest-D_DEBUG-g-I../include/-L../lib-lmylibtest.c
上面的命令编译连接并生成可执行文件test。-D选项定义了一个_DEBUG宏,-g指定生
成调试信息,-I和-L分别指定了一个额外的头文件路径和一个额外的函数库路径,-l
指定了一个连接时的搜索函数库。
13.3.3GNU的make工具
在大型的开发项目中,通常有几十到上百个的源文件,如果每次均手工键入gcc命
令进行编译的话,则会非常不方便。因此,人们通常利用make工具来自动完成编译工作。
这些工作包括:如果仅修改了某几个源文件,则只重新编译这几个源文件;如果某个头文
件被修改了,则重新编译所有包含该头文件的源文件。利用这种自动编译可大大简化开发
工作,避免不必要的重新编译。
实际上,make工具通过一个称为makefile的文件来完成并自动维护编译工作。
makefile需要按照某种语法进行编写,其中说明了如何编译各个源文件并连接生成可执
行文件,并定义了源文件之间的依赖关系。当修改了其中某个源文件时,如果其他源文件
依赖于该文件,则也要重新编译所有依赖该文件的源文件。makefile文件是许多编译器,
包括WindowsNT下的编译器维护编译信息的常用方法,只是在集成开发环境中,用户通
过友好的界面修改makefile文件而已。
默认情况下,GNUmake工具在当前工作目录中按如下顺序搜索makefile:
?GNUmakefile
?makefile
?Makefile
在UNIX系统中,习惯使用Makefile作为makfile文件。如果要使用其他文件作
为makefile,则可利用类似下面的make命令选项指定makefile文件:
$make-fMakefile.debug
13.3.3.1makefile的内容
makefile中一般包含如下内容:
?需要由make工具创建的项目,通常是目标文件和可执行文件。通常使用“目
标(target)”一词来表示要创建的项目。
?要创建的项目依赖于哪些文件。
?创建每个项目时需要运行的命令。
例如,假设你现在有一个C++源文件test.C,该源文件包含有自定义的头文件
test.h,则目标文件test.o明确依赖于两个源文件:test.C和test.h。另外,你可能
只希望利用g++命令来生成test.o目标文件。这时,就可以利用如下的makefile来
定义test.o的创建规则:
#Thismakefilejustisaexample.
#Thefollowinglinesindicatehowtest.odepends
#test.Candtest.h,andhowtocreatetest.o

test.o:test.Ctest.h
g++-c-gtest.C
从上面的例子注意到,第一个字符为#的行为注释行。第一个非注释行指定test.o为
目标,并且依赖于test.C和test.h文件。随后的行指定了如何从目标所依赖的文件建
立目标。
当test.C或test.h文件在编译之后又被修改,则make工具可自动重新编译
test.o,如果在前后两次编译之间,test.C和test.h均没有被修改,而且test.o还
存在的话,就没有必要重新编译。这种依赖关系在多源文件的程序编译中尤其重要。通过
这种依赖关系的定义,make工具可避免许多不必要的编译工作。当然,利用Shell脚本
也可以达到自动编译的效果,但是,Shell脚本将全部编译任何源文件,包括哪些不必要
重新编译的源文件,而make工具则可根据目标上一次编译的时间和目标所依赖的源文件
的更新时间而自动判断应当编译哪个源文件。
一个makefile文件中可定义多个目标,利用maketarget命令可指定要编译的目
标,如果不指定目标,则使用第一个目标。通常,makefile中定义有clean目标,可用
来清除编译过程中的中间文件,例如:
clean:
rm-f*.o
运行makeclean时,将执行rm-f*.o命令,最终删除所有编译过程中产生的所有中
间文件。
13.3.3.2makefile中的变量(宏)
GNU的make工具除提供有建立目标的基本功能之外,还有许多便于表达依赖性关系
以及建立目标的命令的特色。其中之一就是变量或宏的定义能力。如果你要以相同的编译
选项同时编译十几个C源文件,而为每个目标的编译指定冗长的编译选项的话,将是非
常乏味的。但利用简单的变量定义,可避免这种乏味的工作:
#Definemacrosfornameofcompiler
CC=gcc

#DefineamacrofortheCCflags
CCFLAGS=-D_DEBUG-g-m486

#Aruleforbuildingaobjectfile
test.o:test.ctest.h
$(CC)-c$(CCFLAGS)test.c
在上面的例子中,CC和CCFLAGS就是make的变量。GNUmake通常称之为变量,
而其他UNIX的make工具称之为宏,实际是同一个东西。在makefile中引用变量的值
时,只需变量名之前添加$符号,如上面的$(CC)和$(CCFLAGS)。
GNUmake有许多预定义的变量,这些变量具有特殊的含义,可在规则中使用。表13-2
给出了一些主要的预定义变量,除这些变量外,GNUmake还将所有的环境变量作为自己
的预定义变量。
表13-2GNUmake的主要预定义变量
预定义变量
含义
$*
不包含扩展名的目标文件名称。
$+
所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
$<
第一个依赖文件的名称。
$?
所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。
$@
目标的完整名称。
$^
所有的依赖文件,以空格分开,不包含重复的依赖文件。
$%
如果目标是归档成员,则该变量表示目标的归档成员名称。例如,如果目标名称为
mytarget.so(image.o),则$@为mytarget.so,而$%为image.o。
AR
归档维护程序的名称,默认值为ar。
ARFLAGS
归档维护程序的选项。
AS
汇编程序的名称,默认值为as。
ASFLAGS
汇编程序的选项。
CC
C编译器的名称,默认值为cc。
CFLAGS
C编译器的选项。
CPP
C预编译器的名称,默认值为$(CC)-E。
CPPFLAGS
C预编译的选项。
CXX
C++编译器的名称,默认值为g++。
CXXFLAGS
C++编译器的选项。
FC
FORTRAN编译器的名称,默认值为f77。
FFLAGS
FORTRAN编译器的选项。

13.3.3.3隐含规则
GNUmake包含有一些内置的或隐含的规则,这些规则定义了如何从不同的依赖文件
建立特定类型的目标。GNUmake支持两种类型的隐含规则:
?后缀规则(SuffixRule)。后缀规则是定义隐含规则的老风格方法。后缀规则
定义了将一个具有某个后缀的文件(例如,.c文件)转换为具有另外一种后缀
的文件(例如,.o文件)的方法。每个后缀规则以两个成对出现的后缀名定义,
例如,将.c文件转换为.o文件的后缀规则可定义为:
.c.o:
$(CC)$(CFLAGS)$(CPPFLAGS)-c-o$@$<
?模式规则(patternrules)。这种规则更加通用,因为可以利用模式规则定义
更加复杂的依赖性规则。模式规则看起来非常类似于正则规则,但在目标名称
的前面多了一个%号,同时可用来定义目标和依赖文件之间的关系,例如下面
的模式规则定义了如何将任意一个X.c文件转换为X.o文件:
%.c:%.o
$(CC)$(CFLAGS)$(CPPFLAGS)-c-o$@$<
13.3.3.4makefile实例
下面给出一个makefile的实例,见清单13-3。
清单13-3一个makfile文件的例子
―――――――――――――――――――――――――――――――――――――――
#----------------------------------------------------------------------
#MakefilefortestprogramsofMiniGUI4forLinux.
#Copyright(c)1999,WeiYongming.
#
#----------------------------------------------------------------------

#Thesemaybechanged,ifdesired
CC=gcc
CCOPTS=-O2-Wall-m486-D_REENTRANT

#-------------------------------------------------------------------#

CFLAGS=$(CCOPTS)
TESTPROG=testgui
TESTOBJ=testgui.omisc.ogdi.ocursor.oevent.odesktop.o/
window.omain.ocliprect.omenu.ofixstr.otimer.odti.o

all:$(TESTPROG)

$(TESTPROG):$(TESTOBJ)
$(CC)-o$(TESTPROG)$(TESTOBJ)-lvgagl-lvga-lpthread

clean:
rm-f*.o*~.*.swp.*.swo

testgui.o:window.hevent.hmisc.hgdi.hcursor.hmain.hcommon.h
cliprect.o:cliprect.hcommon.h
misc.o:misc.hgdi.hcliprect.hcommon.h
gdi.o:gdi.hcliprect.hmisc.hcommon.h
cursor.o:cursor.hgdi.hcliprect.hmisc.hcommon.h
event.o:event.hcursor.hmisc.hcommon.h
desktop.o:menu.hfixstr.hcliprect.hwindow.hevent.hcursor.h/
gdi.hmisc.hcommon.h
window.o:menu.hfixstr.hcliprect.hwindow.hevent.hcursor.h/
gdi.hmisc.hcommon.h
menu.o:menu.hfixstr.hcliprect.hwindow.hevent.hcursor.h/
gdi.hmisc.hcommon.h
fixstr.o:fixstr.hcommon.h
timer.o:timer.hwindow.hgdi.hcliprect.hinline.hcommon.h
dti.o:menu.hwindow.hgdi.hcliprect.hinline.hcommon.h
main.o:main.hcommon.h
―――――――――――――――――――――――――――――――――――――――
在上面的makefile文件中,定义了多个目标以及和其他源文件的依赖关系。我们可
以注意到上述makefile中定义了一些变量,同时使用GNUmake的内置隐含规则编译各
个.c源文件。执行make命令时的输出如下:
[WeiYM@rocketsrc]$makeclean
rm-f*.o*~.*.swp.*.swo
[WeiYM@rocketsrc]$make
gcc-O2-Wall-m486-D_REENTRANT-ctestgui.c-otestgui.o
gcc-O2-Wall-m486-D_REENTRANT-cmisc.c-omisc.o
gcc-O2-Wall-m486-D_REENTRANT-cgdi.c-ogdi.o
gcc-O2-Wall-m486-D_REENTRANT-ccursor.c-ocursor.o
gcc-O2-Wall-m486-D_REENTRANT-cevent.c-oevent.o
gcc-O2-Wall-m486-D_REENTRANT-cdesktop.c-odesktop.o
gcc-O2-Wall-m486-D_REENTRANT-cwindow.c-owindow.o
gcc-O2-Wall-m486-D_REENTRANT-cmain.c-omain.o
gcc-O2-Wall-m486-D_REENTRANT-ccliprect.c-ocliprect.o
gcc-O2-Wall-m486-D_REENTRANT-cmenu.c-omenu.o
gcc-O2-Wall-m486-D_REENTRANT-cfixstr.c-ofixstr.o
gcc-O2-Wall-m486-D_REENTRANT-ctimer.c-otimer.o
gcc-O2-Wall-m486-D_REENTRANT-cdti.c-odti.o
gcc-otestguitestgui.omisc.ogdi.ocursor.oevent.odesktop.owindow.omain.d
13.3.3.5make命令的常用选项
我们知道,直接在make命令的后面键入目标名可建立指定的目标,如果直接运行
make,则建立第一个目标。我们还知道可以用make-fmymakefile这样的命令指定make
使用特定的makefile,而不是默认的GNUmakefile、makefile或Makefile。但GNUmake
命令还有一些其他选项,表13-3给出了这些选项。
表13-3GNUmake命令的常用命令行选项
命令行选项
含义
-CDIR
在读取makefile之前改变到指定的目录DIR。
-fFILE
以指定的FILE文件作为makefile。
-h
显示所有的make选项。
-i
忽略所有的命令执行错误。
-IDIR
当包含其他makefile文件时,可利用该选项指定搜索目录。
-n
只打印要执行的命令,但不执行这些命令。
-p
显示make变量数据库和隐含规则。
-s
在执行命令时不显示命令。
-w
在处理makefile之前和之后,显示工作目录。
-WFILE
假定文件FILE已经被修改。

13.3.4GNU的调试器
GNU的调试器称为gdb,该程序是一个交互式工具,工作在字符模式。在XWindow系
统中,有一个gdb的前端图形工具,称为xxgdb。gdb是功能强大的调试程序,可完成
如下的调试任务:
?设置断点;
?监视程序变量的值;
?程序的单步执行;
?修改变量的值。
在可以使用gdb调试程序之前,必须使用-g选项编译源文件。可在makefile中
如下定义CFLAGS变量:
CFLAGS=-g
13.3.4.1运行gdb
运行gdb调试程序时通常使用如下的命令:
gdbprogname
执行上述命令之后,gdb将显示一个命令提示行:
[WeiYM@rocketsrc]$gdbtestgui
GNUgdb4.17.0.4withLinux/x86hardwarewatchpointandFPUsupport
Copyright1998FreeSoftwareFoundation,Inc.
GDBisfreesoftware,coveredbytheGNUGeneralPublicLicense,andyouare
welcometochangeitand/ordistributecopiesofitundercertainconditions.
Type"showcopying"toseetheconditions.
ThereisabsolutelynowarrantyforGDB.Type"showwarranty"fordetails.
ThisGDBwasconfiguredas"i386-redhat-linux"...
(nodebuggingsymbolsfound)...
(gdb)
(gdb)就是gdb的提示符。在gdb提示符处键入help,将列出命令的分类,主要的分
类有:
?aliases:命令别名
?breakpoints:断点定义;
?data:数据查看;
?files:指定并查看文件;
?internals:维护命令;
?running:程序执行;
?stack:调用栈查看;
?statu:状态查看;
?tracepoints:跟踪程序执行。
键入help后跟命令的分类名,可获得该类命令的详细清单。例如,键入helpstack,
将得到如下的命令清单:
(gdb)helpstack
Examiningthestack.
Thestackismadeupofstackframes.Gdbassignsnumberstostackframes
countingfromzerofortheinnermost(currentlyexecuting)frame.

(linesdeleted)

Listofcommands:

backtrace--Printbacktraceofallstackframes
bt--Printbacktraceofallstackframes
down--Selectandprintstackframecalledbythisone
frame--Selectandprintastackframe
return--Makeselectedstackframereturntoitscaller
select-frame--Selectastackframewithoutprintinganything
up--Selectandprintstackframethatcalledthisone
要退出gdb时,只需在提示符处键入q并按Enter键。
表13-4列出了常用的gdb命令。
表13-4常用的gdb命令
命令
解释
breakNUM
在指定的行上设置断点。
bt
显示所有的调用栈郑该命令可用来显示函数的调用顺序。
clear
删除设置在特定源文件、特定行上的断点。其用法为:clearFILENAME:NUM。
continue
继续执行正在调试的程序。该命令用在程序由于处理信号或断点而导致停止运行时。
displayEXPR
每次程序停止后显示表达式的值。表达式由程序定义的变量组成。
fileFILE
装载指定的可执行文件进行调试。
helpNAME
显示指定命令的帮助信息。
infobreak
显示当前断点清单,包括到达断点处的次数等。
infofiles
显示被调试文件的详细信息。
infofunc
显示所有的函数名称。
infolocal
显示当函数中的局部变量信息。
infoprog
显示被调试程序的执行状态。
infovar
显示所有的全局和静态变量名称。
kill
终止正被调试的程序。
list
显示源代码段。
make
在不退出gdb的情况下运行make工具。
next
在不单步执行进入其他函数的情况下,向前执行一行源代码。
printEXPR
显示表达式EXPR的值。
quit
退出gdb。
setvariable
设置变量的值,语法为setvariableVAR=VALUE。
shellCMD
在不退出gdb的情况下执行Shell命令。
step
向前执行一行源代码,如果可能,跳入调用函数执行。
watchVAR
当变量VAR的值发生变化时,显示该变量的值。
where
显示调用序列。根据该命令的输出,可查找程序出问题的地方。
x/FADDR
检查内存地址为ADDR的内容,F指定显示的格式。可取
x:十六进制o:八进制d:十进制
u:无符号十进制t:二进制f:浮点数等。
ADDR一般取变量名称或指针名称。

13.3.4.2使用gdb
为了说明gdb的使用,我们编写一段有问题的程序,见清单13-4。
清单13-4一个有错误的C源程序test.c
―――――――――――――――――――――――――――――――――――――――
#include<stdio.h>
#include<stdlib.h>

staticcharbuff[256];
staticchar*string;
intmain()
{

printf("Pleaseinputastring:");
gets(string);

printf("/nYourstringis:%s/n",string);
}
―――――――――――――――――――――――――――――――――――――――
上面这个程序非常简单,其目的是接受用户的输入,然后将用户的输入打印出来。该
程序使用了一个未经过初始化的字符串地址string,因此,编译并运行之后,将出现
SegmentFault错误:
[WeiYM@versagcc]$gcc-otest-gtest.c
[WeiYM@versagcc]$./test
Pleaseinputastring:asfd
Segmentationfault(coredumped)
为了查找该程序中出现的问题,我们利用gdb,并按如下的步骤进行:
1.运行gdbtest命令,装入test可执行文件;
2.执行装入的test命令:
(gdb)run
Startingprogram:/home/WeiYM/projects/study/gcc/test
Pleaseinputastring:abcd

ProgramreceivedsignalSIGSEGV,Segmentationfault.
0x4006a7dain_IO_gets(buf=0x0)atiogets.c:55
iogets.c:55:Nosuchfileordirectory.
3.使用where命令查看程序出错的地方:
(gdb)where
#00x4006a7dain_IO_gets(buf=0x0)atiogets.c:55
#10x8048413inmain()attest.c:11
#20x40030cb3in__libc_start_main(main=0x80483f8<main>,argc=1,
argv=0xbffffca4,init=0x80482bc<_init>,fini=0x804845c<_fini>,
rtld_fini=0x4000a350<_dl_fini>,stack_end=0xbffffc9c)
at../sysdeps/generic/libc-start.c:78
where命令的输出显示了函数的调用顺序,最近一次函数调用,即#0调用是C库
函数gets。该函数从test.c的第11行处调用(main函数)。
4.利用list命令查看调用gets函数附近的代码:
(gdb)listtest.c:11
6
7intmain()
8{
9
10printf("Pleaseinputastring:");
11gets(string);
12
13printf("/nYourstringis:%s/n",string);
14}
15
5.唯一能够导致gets函数出错的因素就是变量string。用print命令查看string
的值:
(gdb)printstring
$1=0x0
显然,为gets函数传递了一个空指针,这就是程序的错误所在。这是因为string是
一个全局变量,运行时初始化为0,即空指针。
6.在gdb中,我们可以直接修改变量的值,只要将string取一个合法的指针值就
可以了,为此,我们在第11行处设置断点:
(gdb)break11
Breakpoint1at0x8048408:filetest.c,line11.
(gdb)run
Theprogrambeingdebuggedhasbeenstartedalready.
Startitfromthebeginning?(yorn)y
Startingprogram:/home/WeiYM/projects/study/gcc/test

Breakpoint1,main()attest.c:11
11gets(string);
7.程序重新运行到第11行处停止,这时,我们可以用setvariable命令修改string
的取值:
(gdb)setvarstring=buff
8.然后继续运行,将看到正确的程序运行结果:
(gdb)cont
Continuing.
Pleaseinputastring:abcd

Yourstringis:abcd

Programexitedwithcode026.
上面的例子足以说明gdb的一般使用方法,当然,gdb的命令很多,还可用来调试
多线程程序。这些命令的使用,需要读者在实践中学习并掌握。
13.3.5版本控制工具
在小型软件的开发过程中,利用make工具可自动完成一些编译工作。如果涉及到的
开发人员不多,代码的维护是简单的。人们通常利用备份和强制性的代码注释来维护源程
序的修改。但在大型项目中,开发人员和代码规模非常大的情况下,就有必要采用专门的
版本控制软件来维护代码的修订。在Microsoft的开发工具中,有一个称为SourceSafe的
软件,专门用来进行版本控制,在PowerBuilder这样的大型开发工具中,也通常有内置
的版本控制系统。在Linux上,通常使用的版本控制系统叫RCS,即RevisionControl
System(修订控制系统)。
利用版本控制系统,开发人员能够在需要时恢复某个文件的特定版本或修订。在使用
版本控制系统时,开发人员对一个文件的修改工作按如下的步骤进行:
1.当开发人员有一个初始的源文件版本时,将该文件归档到版本控制系统中。
2.当开发人员需要对某个文件进行修改时,首先要获得当前版本的一个拷贝。在某
个开发人员获得该文件的一个拷贝时,版本控制系统将确保其他人不能获得该文件。
3.当修改完源文件,并经过测试之后,开发人员将该文件保存为一个新的版本。
4.其后需要修改该文件时,将始终在该文件的最新版本的基础上进行修改。
为此,RCS提供了许多工具帮助维护源代码的修订,主要的工具见表13-5。
表13-5RCS的主要工具
工具名称
功能
ci
建立某个文件的新修订或将某个工作文件添加到RCS文件中。
co
为只读目的获取某个文件的一个工作版本。(co-l可提供一个工作文件并锁定原有
的文件,这样就可以修改工作文件。)
ident
在文件中搜索标识符。
merge
将两个文件合并成为第三个文件。
rcsdiff
就工作文件和RCS维护的文件进行比较。
rcsmerge
合并某个文件的不同版本。
rlog
查看某个文件的修改日志。

限于篇幅不能详细讲解RCS的使用,详细信息可参阅rcsintro(1)手册页以及相
关帮助信息。
13.3.6Perl简介
Linux为开发人员提供了广阔的活动舞台。开发人员不仅能够利用C/C++、Fortran、
Pacal、Lisp等多种编程语言编写程序,而且能够使用各种脚本语言编写程序,其中包括
Shell脚本、Perl脚本、Tcl/Tk脚本等。通常说来,脚本语言就是一种编程语言,但它
更加贴近自然语言,具有简单、方便的特点,并且在某些方面,比起传统的编程语言来说
更加有优势。
本小节将简单描述在Linux中广为使用的脚本语言Perl。Perl,即Practical
ExtractionReportLanguage(实用析取报表语言),由LarryWall创立,可用来从文
本文件中析取信息,并利用这些信息建立报表。由于Perl和Linux一样,也是自由软
件,因此,Perl在各种计算机系统中广为流传。各种Linux商业发行版本中均包含有
Perl。
13.3.6.1Perl脚本的执行
和传统的编译语言不同,Perl脚本一般是一些文本文件,由perl程序解释并执行
Perl脚本。例如,下面是一个简单的Perl脚本:
#!/usr/bin/perl
#Thisisacomment.
print"Hello,World!/n"
该脚本的第一行利用#!指定了脚本的解释程序为/usr/bin/perl,第二行是一个注
释行,第三行打印“Hello,World!”。
执行该脚本之前,首先要将脚本文件设置为可执行模式。如果脚本名称为hello.pl
(.pl一般是Perl脚本的后缀名),则使用如下的命令:
$chmod+xhellol.pl
然后可在命令行直接执行hello.pl:
$./hello.pl
实际上,Linux将按如下命令执行脚本:
/usr/bin/perl./hello.pl
13.3.6.2Perl的基本语法
Perl包含有与其他语言一样的共同特色:
?用来保存不同类型数据的变量。
?利用运算符将变量组合而成的表达式。
?执行动作的表达式。
?可控制语句执行路线的流控制语句。
?划分功能,提供可重用性的函数(子例程或例程)。
Perl程序的书写形式是自由的,和C类似,每个Perl语句以分号(;)结束,#代
表一个注释行,Perl也用与C一样的大括号({...})来定义语句组。
下面的小节讲述Perl变量、运算符和表达式、语句、流控制以及其他的特性。
13.3.6.3变量
在Perl程序中,不必显示定义变量,所有的变量以@、$或%打头,分别代表了Perl
的三种变量类型。
?标量型变量(ScalarVariable)。标量型变量定义了基本的数据类型,包括整
数、浮点数和字符串。标量型变量由$字符开头:
$title="LinuxAndWindowsNT";
$count=10;
?数组型变量(ArrayVariable)。数组型变量是标量型变量的集合,数组型变量
的前缀是@,如下所示:
@commands=("ls","dir","cd","rm","mv");
@length=(2,3,2,2,2);
可通过类似C语言的下标方法访问数组中的元素,例如:
$commands[0]
?关联型数组(AssociateArray)。关联型数组是一些键/值对的集合,其中的键
是字符串,而值可以是任意一种标量型变量。关联型数组的前缀是%。利用关
联型数组,可通过字符串键而快速查找对应的值,如下所示:
%grades=("WeiYM",60,"ZhengX",100,"other",50);
利用{key}可针对关联数组中的键进行求值:
$mygrade=$grads{WeiYM}
需要注意的是,在语句中,当变量处在双引号("")之中时,Perl将对变量求值,
而当变量处在单引号('')中时,Perl不会对变量求值。下面的两个语句将输出不同的
内容:
print"Mygradeis$grads[WeiYM]/n";#Output:Mygradeis60
print'Mygradeis$grads[WeiYM]/n';#Output:Mygradeis$grads[WeiYM]/n
Perl有一些预定义的变量,包括:
?@ARGV:字符串数组,包含脚本的字符串选项。类似C语言main函数的argv参
数。
?%EVN:包含环境变量的关联型数组。例如,$EVN{HOME}是用户主目录。
?$$:脚本的进程ID。
?$<:运行脚本的用户ID。
?$?:最近一次系统调用的返回状态。
?$_:许多函数的默认参数。
?$0:脚本名称。
13.3.6.4运算符和表达式
运算符用来组合和比较Perl变量,Perl提供了几乎和C一样的运算符。其中包括
+、-、*、/等算术运算符,以及>、<、>=、<=、==等逻辑运算符,上面看到的[]实际
是Perl的索引运算符。
但是,C语言定义的运算符非常基本,我们不能用==等运算符比较字符串。在Perl
中也一样,为了比较字符串,Perl定义了类似FORTRAN的运算符eq来比较两个字符串
是否相等:
if($inputeq"quit"){
exit;
}
其他类似FORTRAN的比较运算符有:ne(不相等)、lt(小于)、gt(大于)、le(小于或
等于)、ge(大于或等于)。也可以使用cmp运算符比较字符串,将返回-1、0、1等值,
返回值与C函数strcmp的返回值类似。
Perl提供的其他独有的运算符有:
?**:指数运算符,如:
$y**=2;
?():空数组运算符,可用来初始化数组变量,如:
@commands=();
?.:字符串连接运算符,可用来连接两个字符串变量,如:
$message=$part1.$part2;
?x=:重复运算符,可利用该运算符重复字符串指定的次数,如:
$marker="*";
$markerx=80;#$maker将包含65个*
?..:范围运算符,可利用该运算符方便地初始化字符串,如:
$alphabet=('A'..'Z');
在第八章中我们了解了正则表达式。在Perl中,正则表达式得到了非常广泛的使用。
例如:
if($name=~/[wW]ei[yY][mM]/){
print"$nameisabadman./n";
}
将在$name取“Weiym”或“weiym”时执行打印语句。注意在上述语句中,正则表达式
由//包围,检查是否匹配时,采用=~运算符(检查不匹配时,用!~运算符)。
13.3.6.5流控制语句
Perl提供了和C语言一样的流控制语句,并且包含一些额外的特色。Perl的条件
语句的形式如下:
conditional-statement{
Perlcodetoexecuteifconditionalistrue
}
注意,包围执行代码的大括号是必须存在的,这点和C语言不同。值得指出的是,Perl
和C一样,将任何非零的值看成是true,而将零看成是false。
表13-6简要给出了Perl的流控制语句。
表13-6Perl的流控制语句及其用法
流控制语句
说明
举例
if
和C语言的if语句类似。可使用elseif
和else分句。
if($usereq"root"){
print"thesuperuser./n";
}else{
print"anormaluser./n";
}
unless
和if语句一样的使用形式,包括elseif和
else分句。其判断结果和if相反。
unless($usereq"root"){
print"anormaluser./n";
}
while
和C语言的while语句类似。和C语言
的break和continue语句对应的分别是
last和next语句。
while(<STDIN>){
print$_;
}
for
和C语言的for语句语法类似。
for($i=0,$sum=0;$i<=10;$i++){
$sum+=$i;
}
foreach
可用来处理数组的特殊for语句。
foreach$i(1..10)(
$sum+=$i;
}
goto
goto语句的使用完全和C语言一样。
ReDo:
if(<STDIN>){
print$_;
gotoReDo;
}

13.3.6.6访问系统
Perl可通过如下途径执行Linux命令:
?调用system,传递要执行的Linux命令。
?在一对反引号(`)中指定Linux命令。
?调用fork函数,类似Linux的fork系统调用。将复制当前脚本并在子进程
中处理新的命令。
?调用exec函数,类似Linux的exec系统调用。将以新脚本或命令覆盖当前
脚本。
?使用fork和exec来提供类似Shell的功能(通过子进程监视用户的输入并
处理用户命令)。
我们举一个例子说明了上述最后一种命令执行途径,见清单13-5。
清单13-5psh.pl――使用fork和exec来执行用户命令
―――――――――――――――――――――――――――――――――――――――
#!/usr/bin/perl

#该脚本使用"fork"和"exec"
#来执行由用户键入的命令

$prompt="Command(/"exit/"toquit):";
print$prompt;

while(<STDIN>){
chop;#清除尾部的/n字符
if($_eq"exit"){exit0;}

$status=fork;
if($status){
#这是父进程,等待子进程执行结束
wait;
print$prompt;
next;
}else{
exec$_;
}
}
―――――――――――――――――――――――――――――――――――――――
执行上述脚本,将得到如下结果:
[WeiYM@versaperl]$./psh.pl
Command("exit"toquit):ps
PIDTTYTIMECMD
670pts/100:00:00bash
807pts/100:00:00psh.pl
808pts/100:00:00ps
Command("exit"toquit):exit
13.3.6.7文件处理
从前面的例子中可以看到,Perl使用STDIN作为标准输入的标识符。在Perl中,
文件是由句柄代表的,通常用大写表示。除STDIN之外,Perl中还有两个预定义的文件
句柄,即STDOUT和STDERR,分别代表标准输出设备和错误输出设备。
如果要使用其他文件,则可以利用open函数打开文件,用close函数关闭文件,
而用<FILEHANDLE>来从一个文件中读取一行。例如:
open(PWDFILE,"/etc/passwd");
while(<PWDFILE>){print$_;}
closePWDFILE;
13.3.6.8用户自定义例程
Perl中包含有非常多的内置函数,但用户也可以自定义例程。在Perl脚本中使用
例程非常简单:
#!/usr/bin/perl
subdisplay
{
#将函数参数复制到局部变量
local($name,$x)=@_;
print"Gradeof$nameis$x/n";
}


%grades=("WeiYM",60,"ZhengX",100,"other",50);

&display("WeiYM",$grades{WeiYM});
&display("ZhengX",$grades{ZhengX});
&display("other",$grades{other});
上述脚本的运行结果如下:
[WeiYM@versaperl]$./grad.pl
GradeofWeiYMis61
GradeofZhengXis100
Gradeofotheris50
Perl为用户提供了160多个内置的函数,其中一些类似于C标准库函数中的函数,
其他一些则是用于访问操作系统的函数。有关内置函数的详细内容可参阅perlfunc手册
页。其他有关Perl的信息也被划分为单独的手册页,概要信息可参阅perl手册页。
13.4有关编程风格
编写这一小节的目的是提醒读者在编程过程中注意编程风格。如果你只是在编写一些
小的练习程序,程序只有一两百行长的话,编程风格可能并不重要。然而,如果你和许多
人一起进行开发工作,或者,你希望在过一段时间之后,还能够正确理解自己的程序的话,
就必须养成良好的编程习惯。在诸多编程习惯当中,编程风格是最重要的一项内容。
良好的编程风格可以在许多方面帮助开发人员。如果你阅读过Linux内核源代码的
话,可能会对程序的优美编排所倾倒。良好的编程风格可以增加代码的可读性,并帮助你
理清头绪。如果程序非常杂乱,大概看一眼就该让你晕头转向了。编程风格最能体现一个
程序员的综合素质。
但是,如果比较Windows和Linux两个系统下的程序,就会发现在编程风格上,这
两个系统有比较大的差别。
许多读者可能对Windows所推崇的匈牙利命名法很熟悉。这种方法定义了非常复杂
的函数、变量、类型等的命名方法,典型的命名方法是采用大小写混写的方式,对于变量
名称,则采用添加前缀的办法来表示其类型,例如:
charszBuffer[20];
intnCount;
利用sz和n分别代表字符串和整数。为了表示一个变量名称,采用如下的变量名称是
可能的:
intiThisIsAVeryLongVariable;
在Win32API中,具有复杂名称和调用参数的函数屡见不鲜,例如:
BOOLGetSecurityDescriptorControl(
PSECURITY_DESCRIPTORpSecurityDescriptor,
PSECURITY_DESCRIPTOR_CONTROLpControl,
LPDWORDlpdwRevision);
而在Linux中,我们经常看到的是定义非常简单的函数接口和变量名称。在Linux内
核的源代码中,可以看到Linux内核源代码的编码风格说明(<linux_source>/
Documentation/CodingStyle)。我们曾多次提到过UNIX系统的精巧特点以及积木式的设
计原则。C语言最初来自UNIX操作系统,与UNIX的设计原则一样,C语言被广泛认可
和使用的一个重要原因是它的灵活性以及简洁性。因此,在利用C语言编写程序时,始
终应当符合其简洁的设计原则,而不应当使用非常复杂的变量命名方法。Linus为Linux内
核定义的C语言编码风格要点如下:
1.缩进时,使用长度为8个字符宽的Tab键。如果程序的缩进超过3级,则应考
虑重新设计程序。
2.大括号的位置。除函数的定义体外,应当将左大括号放在行尾,而将右大括号放
在行首。函数的定义体应将左右大括号放在行首。如下所示:
intfunction(intx,inty)
{
if(x==y){
...
}elseif(x>y){
...
}else{
...
}

return0;
}
3.应采用简洁的命名方法。对变量名,不赞成使用大小写混写的形式,但鼓励使用
描述性的名称;尽可能不使用全局变量;不采用匈牙利命名法表示变量的类型;采用短小
精悍的名称表示局部变量;保持函数短小,从而避免使用过多的局部变量。
4.保持函数短小精悍。
5.不应过分强调注释的作用,应尽量采用好的编码风格而不是添加过多的注释。
13.5小结
在应用软件开发方面,Linux和WindowsNT在许多方面存在着差别,本章从操作系
统接口开始,简单比较了Linux和WindowsNT在系统接口、函数库、编程工具和编码
风格等方面的不同。考虑到许多读者将在Linux上开展应用软件的开发,因此,本章重
点讲解了Linux上进行软件开发时主要使用的开发工具,其中包括:
?GNUC、C++编译器
?GNUmake工具
?GNU的调试器
?RCS版本控制系统
本章还简单介绍了Linux系统上广为流行的Perl脚本语言。
实际上,编程的概念在Linux系统无处不在,许多工具均提供了类似编程语言一样
的变量定义、语法、规则等概念。因此,Linux系统为程序员提供了更加广阔的舞台。
不管在Linux还是在WindowsNT上,图形用户界面编程是软件开发中一个非常重
要的话题。下一章将重点就Linux上的XWindow软件开发以及WindowsNT的应用软
件开发作一比较。