[转载]Linux 下的编程工具

来源:互联网 发布:淘宝号怎么注册账号 编辑:程序博客网 时间:2024/05/07 09:23

BBS水木清华站∶精华区

发信人: Kongming (没日没夜...), 信区: Linux        
标  题: Re: 如何学习linux下编程? 
发信站: BBS 水木清华站 (Mon Aug  2 11:27:04 1999) 
 
我为你贴一篇文章吧,简要介绍了 Linux 下的编程工具, 
入门是没有问题的。 
 
这是我最近编写的书的部分内容。 
 
严正声明: 
本文由 Kongming 网友享有专有版权。 
版权所有,未经许可,不准随意复制、改编或变相改编、扩散。 
 
主要包含如下内容: 
? GNU C、C++ 编译器 
? GNU make 工具 
? GNU 的调试器 
? RCS 版本控制系统 
并简单介绍了 Linux 系统上广为流行的 Perl 脚本语言。 
对比了 Linux 和 Windows NT 两种不同的编码风格。 
 
在 BBS 上看有些困难,拿回家慢慢看吧。 
 
 
 
第十三章  软件开发 
由于操作系统只是一个工具,对大多数读者来说,学习操作系统的最终目的是利用操 
作系统上的应用软件完成自己的工作,或者利用开发工具开发应用程序。因此,对一个操 
作系统来说,可获得的开发工具,以及这些开发工具是否有效,就成为评价操作系统的一 
个重要因素。 
这一部分的后三章将主要从开发人员的角度比较 Linux 和 Windows NT。本章的主要 
内容如下: 
? 介绍操作系统为开发人员提供的各种系统服务; 
? 介绍操作系统上的主要开发工具。 
考虑到许多读者可能对 Linux 上的开发工具了解不多,因此,本章将介绍一些 Linux  
上常用的开发工具。当然,软件开发并不是区区一章就能够讲得清楚的,更多的需要读者 
自己去实践。 
13.1  POSIX 标准和操作系统接口 
13.1.1  Linux 和 Windows NT 对 POSIX 标准的支持 
从第一章的内容我们知道,POSIX 是一种标准,主要定义了操作系统的系统调用接口, 
POSIX 标准最初为提高 UNIX 系统之间的移植性而开发。Linux 是一种 POSIX 兼容系统, 
而 Windows NT 中包含了 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 标准;而 Windows NT 只提供对 POSIX.1 的支持,而且是一个不完 
整的系统,许多方面(例如 I/O)需要 Win32 API 来完成。实际上,Windows NT 的许多  
POSIX.1 接口是通过 Win32 API 间接实现的。 
Windows NT 中的 POSIX.1 版本基本上没有什么用处,在 Visual Studio 6.0 中, 
甚至取消了对 POSIX.1 的支持。如果读者要在 Windows NT 上进行一些 POSIX 的开发工 
作,建议不要使用 Windows NT POSXI.1。用户可以使用第三方POSIX 系统,或者由 GNU 提 
供的 GNU Win32 工具。这些产品提供了更强大的功能,例如 Software System Inc. 的  
POSIX.2 工具箱(OpenNT)具有如下特征:真正的 UNIX Shell,以及在 Windows NT 中 
运行 X Window 的能力等。这些软件为 Windows NT 的非完整 POSIX.1 系统增加了如下 
特性: 
? POSIX.1、POSIX.2 和 ANSI C 接口; 
? 可在 Intel 和 Alpha 平台上使用; 
? BSD 套接字支持; 
? System V 的共享内存和信号灯 IPC 机制; 
? X11R5 客户、库和头文件; 
? OPENNTIF(OSF/Motif 1.2.4 窗口管理器和开发库); 
? Color curses库; 
? 完整的 Shell 作业管理; 
? 磁带设备支持; 
? UNIX 开发工具:make、rcs、yacc、lex、cc、c89、nm、ar、strip 等。 
GNU Win32 是针对 x86 和 PowerPC 的 Windows NT/95 的 GNU 开发工具。使用这些 
开发工具开发的应用程序能够访问 Microsoft Win32 API,也能访问 Cygwin32 API,它 
提供了附加的类似 UNIX 的功能,包括 UNIX 套接字和进程控制等。利用这些工具,可增 
强 Windows NT 上 POSIX 子系统: 
? 利用标准的 Microsoft Win32 API 或/和 Cygwin32 API 编写 Win32 控制台或  
GUI应用程序; 
? 能够方便地从源代码中配置和建立许多 GNU 工具,包括 GNU Win32 开发工具 
本身; 
? 可将许多重要的 UNIX 工具移植到 Windows NT 而不用对源代码进行大规模修 
改; 
? 包含有相当完整的 UNIX 环境工具,可使用许多普通的 UNIX 系统工具。 
有关上述这两种软件,读者可访问如下站点: 
http://www.softway.com/OpenNT/homet.htm 
htt://www.cygus.com/misc/gnu.win32 
和 Windows NT 相反的是,Linux 是一个 POSIX.1标准的完全兼容系统。Linux 上 
的 POSIX.2 标准实现是由 GNU 工具和函数库提供的,其中最主要的 C 函数库是 glibc。 
该函数库包含如下内容: 
? ISO C:C 编程语言的国际标准。GNU C 函数库与美国国家标准局(ANSI)公布 
的 C 标准兼容。这一标准后来被国际标准化组织所接收(ISO/IEC 9899:1990)。 
? POSIX:操作系统的 ISO/IEC 9945(即 IEEE 1003)标准。GNU C 函数库实现了  
ISO/IEC 9945-1:1996 (POSIX 系统应用程序编程接口,即 POSIX.1)指定的 
所有函数。该标准是对 ISO C 的扩展,包括文件系统接口原语、设备相关的终 
端控制函数以及进程控制函数。同时,GUN C 函数库还支持部分由 ISO/IEC  
9945-2:1993(POSIX Shell 和 工具标准,即 POSIX.2)指定的函数,其中包 
括用于处理正则表达式和模式匹配的函数。 
? Berkeley Unix:BSD 和 SunOS。GNU C 函数库定义了某些 UNIX 版本中尚未标 
准化的函数,尤其是 4.2 BSD, 4.3 BSD, 4.4 BSD Unix 系统(即“Berkeley Unix”) 
以及“SunOS”(大众化的 4.2 BSD 变种,其中包含有某些Unix System V 的功 
能)。BSD 函数包括符号链接、select 函数、BSD 信号处理函数以及套接字等 
等。 
? SVID:System V 的接口描述。System V 接口描述(SVID)是描述 AT&T Unix System  
V 操作系统的文档,它是对 POSIX 标准的扩展超集。GNU C 函数库定义了大多 
数由 SVID 指定而未被 ISO C 和 POSIX 标准指定的函数。来自 System V 的 
支持函数包括进程间通信和共享内存、hsearch 和 drand48 函数族、fmtmsg 以 
及一些数学函数。 
? XPG:X/Open 可移植性指南。X/Open 可移植性指南(由 X/Open Company, Ltd. 
出版),是比 POSIX 更为一般的标准。X/Open 拥有 Unix 的版权,而 XPG 则 
指定成为 Unix 操作系统必须满足的需求。GNU C 函数库遵循 X/Open 可移植 
性指南(Issue 4.2)以及所有的 XSI(X/Open 系统接口)兼容系统的扩展, 
同时也遵循所有的 X/Open Unix 扩展。 
同时,Linux 也包含有许多字符界面管理、网络协议处理以及图形图象处理的函数库, 
这些函数库均作为一般 Linux 商业发行版的一部分而一同发行。有关这些函数库的详细 
介绍,读者可参阅附录 C。 
实际上,Microsoft 并没有打算自己的操作系统做到与 UNIX 完全兼容,如果读者看 
到过由微软(中国)有限公司编著的《Microsoft SQL Server 6.5 技术参考》(科学出版 
社、龙门书局 1997 年出版)一书,就可以明白微软和 UNIX 阵营的激烈竞争。既然  
Microsoft 在该书中说 UNIX 如何如何不好,当然就不会对来自 UNIX 的 POSIX 有什么 
好的支持了。 
那么,Windows NT 的操作系统接口包括哪些内容呢? 
13.1.2  Windows NT 的操作系统接口 
Windows NT 的操作系统服务主要体现在 Win32 API 上,大体可分为如下几类: 
? 窗口管理。应用程序可用来创建和管理窗口的部分,涉及到窗口、消息、消息 
队列、控件、对话框、鼠标和键盘输入、定时器、菜单、键盘加速键以及其他 
与窗口管理有关的内容。 
? 图形设备接口。应用程序用来实现设备无关图形的部分,涉及到设备描述表、 
坐标转换、元文件、位图、图标、画刷、画笔、区域、直线和曲线、字体、颜 
色、路径、剪切、打印等。 
? 系统服务。应用程序用来实现基本多任务管理的 Win32 API 部分,涉及到进程 
和线程、内存管理、网络、动态链接、安全性以及其他有关系统的内容。 
? 多媒体服务。Win32 API 中多媒体支持,涉及到音频、视频、媒体控制、多媒 
体文件输入和输出以及增强的定时器功能等。 
? 扩展库。为 Win32 API 额外增加的库,涉及到公用对话框、简化动态数据交换 
(DDE)的管理函数、Shell 中增强的拖放操作、文件安装函数以及数据解压缩 
函数。 
可明显看出,POSIX 和 Win32API 是两个完全不同的操作系统接口体系。如果仅比较 
这两个操作系统为用户提供的接口及函数库的话,可明显看到有如下特点: 
? 基于 POSIX 的操作系统接口部分比 Win32 的系统服务部分要简洁得多。POSIX 的 
接口函数大概只有二百多个;而 Win32 的系统服务部分的函数约有上千个,而 
且调用接口相对复杂。 
? Linux 上除操作系统接口之外的函数库非常丰富。笔者曾经在 MS-DOS、Windows  
3.1、Windows 95/NT 下进行过 C/C++ 的开发工作,当我接触到 Linux 时,发 
现许多原来需要自己编写程序实现的功能,Linux 上已经有的可用的函数库。 
13.2  Visual Studio 和 GNU 
在 Windows NT 上进行开发,用户主要使用的通用开发工具有: 
? Microsoft Visual Studio。该开发工具包是 Microsoft 最完整的开发工具包, 
也是使用最为广泛的开发工具包,其中包含有 Visual C++、Visual Basic、Visual  
FoxPro 等开发工具。这些工具一般是集成开发环境,利用这些工具,可在一个 
程序中完成编辑、编译、调试等各项工作,对提高开发效率有帮助。 
? Borland C++。该工具是 Borland 公司的 C/C++ 编译器,和 Visual C++ 类似, 
也是一个集成环境。 
? Borland Delphi。该工具是 Borland 公司开发的 Pascal 编译器,Borland 公 
司为了和 Visual Basic 竞争而开发,拥有较为广泛的用户群,也是一个集成 
环境。 
Windows NT 上的开发工具都有一个特点,它们均是一些集成开发工具。作为一个程 
序员,笔者对集成开发环境是否能够真正提高开发效率,或者能够在多大程度上提高开发 
效率保持怀疑态度。因为许多程序员并不使用集成的开发环境,但也同样富有效率。Linux  
内核开发小组就是一个例子,在开发操作系统的时候,是无法使用集成开发工具的。使用 
集成开发环境有一个缺点,就是容易让程序员养成懒惰的习惯。笔者就更加愿意在程序出 
问题的时候去认真地分析程序,而不是一味依赖调试工具。 
和 Windows NT 上的集成开发环境不同,Linux 上的开发工具是一些零散的 GNU 工 
具。例如,程序员可能使用 VIM 编写程序,然后用 make 或 gcc 编译程序,如果程序有 
错误,用 gdb 调试程序等。当然,也有一些集成的开发环境可以使用,例如 xwpe(Window  
Programming Environment)就是一个集成开发环境。如果使用 Emacs,程序员也可以借 
助 Emacs 的强大配置能力为自己定制一个集成的开发环境。 
Linux 下的程序开发还有一个特点,就是可以借助许多脚本语言构造复杂的程序。例 
如,程序员可借助 Shell 脚本编写处理文件、目录的脚本程序,也可以利用 Perl 
(Practical Extraction Report Language)编写用来处理文本文件和生成报表的脚本程 
序,还可以利用 Tcl/Tk 的脚本程序编写图形用户界面。如果恰当地使用这些脚本语言, 
可以达到事半功倍的效果。在 Windows NT 下,能够被称为脚本语言的大概算是 VBA(Visual  
Basic for Application),但 VBA 仅在有限的应用软件中支持,例如,Microsoft Office, 
而并不被操作系统支持。现在,Microsoft 正打算将 Perl 的支持添加到它的操作系统中。 
本章将简单介绍一些 Linux 下常见的开发工具以及有关 Perl 脚本编程的知识。由 
于 Tcl/Tk 主要是用来编写图形用户界面的,我们将在下一章讲解 Tcl/Tk 的使用。 
13.3  Linux 上的开发工具 
13.3.1  获取帮助 
在 Linux 上,程序员获取帮助的最常用工具是 man 或 xman。利用 man 或 xman, 
程序员可以查看特定函数的说明。例如,键入 man printf 将显示 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.2  GNU C 和 C++ 编译器 
Linux 中最重要的软件开发工具是 GCC。GCC 是 GNU 的 C 和 C++ 编译器。实际上, 
GCC 能够编译三种语言:C、C++ 和 Object C(C 语言的一种面向对象扩展)。利用 gcc 命 
令可同时编译并连接 C 和 C++ 源程序。 
13.3.2.1  GCC 基本使用 
利用 GCC 编译并连接少数几个 C 源文件是简单的。假设读者的一个程序只有一个源 
文件 hello.c,其内容如下: 
#include <stdio.h> 
#include <stdlib.h> 
 
int main () 

    printf ("Hello, world!/n"); 
 
    return 0; 

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

    if (n <= 1) 
        return 1; 
 
    else 
        return factorial (n - 1) * n; 

――――――――――――――――――――――――――――――――――――――― 
清单 13-2  main.c 
――――――――――――――――――――――――――――――――――――――― 
#include <stdio.h> 
#include <stdlib.h> 
 
 
int factorial (int n); 
 
int main (int argc, char **argv) 

    int n; 
 
    if (argc < 2) { 
        printf ("Usage: %s n/n", argv [0]); 
        return -1; 
    } 
    else { 
        n = atoi (argv[1]); 
        printf ("Factorial of %d is %d./n", n, factorial (n)); 
    } 
 
    return 0; 

――――――――――――――――――――――――――――――――――――――― 
利用如下的命令可编译生成可执行文件,并执行程序: 
[WeiYM@versa gcc]$ gcc -o factorial main.c factorial.c 
[WeiYM@versa gcc]$ ./factorial 5 
Factorial of 5 is 120. 
在上面的 GCC 命令选项中,使用了一个 -o 选项,该选项指定了编译/连接生成的输 
出文件的名称。如果不指定该输出文件的名称,则 GCC 自动建立一个 a.out 文件。 
我们也可以分别对上述两个文件进行编译,然后再连接起来,这时可使用 GCC 的 -c 选 
项,如下所示: 
[WeiYM@versa gcc]$ gcc -c main.c 
[WeiYM@versa gcc]$ gcc -c factorial.c 
[WeiYM@versa gcc]$ gcc -o factorial main.o factorial.o 
[WeiYM@versa gcc]$ ./factorial 10 
Factorial of 10 is 3628800. 
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> 
 
void main (void) 

    cout << "Hello, world!" << endl; 

则可以如下调用 g++ 命令编译、连接并生成可执行文件: 
[WeiYM@versa gcc]$ g++ -o hello hello.c 
[WeiYM@versa gcc]$ ./hello 
Hello, world! 
13.3.2.3  GCC 的其他选项 
gcc 命令的基本语法是: 
gcc options filename 
gcc 的选项一般以减号(-)开头,有短的选项,例如上面看到的 -o 或 -c,也有长 
的选项。表 13-1 给出了 gcc 命令的常用选项。 
表 13-1  gcc 命令的常用选项 
选项 
解释 
-ansi 
只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU C 的某些特色, 
例如 asm 或 typeof 关键词。 
-c 
只编译并生成目标文件。 
-DMACRO 
以字符串“1”定义 MACRO 宏。 
-DMACRO=DEFN 
以字符串“DEFN”定义 MACRO 宏。 
-E 
只运行 C 预编译器。 
-g 
生成调试信息。GNU 调试器可利用该信息。 
-IDIRECTORY 
指定额外的头文件搜索路径DIRECTORY。 
-LDIRECTORY 
指定额外的函数库搜索路径DIRECTORY。 
-lLIBRARY 
连接时搜索指定的函数库LIBRARY。 
-m486 
针对 486 进行代码优化。 
-o FILE 
生成指定的输出文件。用在生成可执行文件时。 
-O0 
不进行优化处理。 
-O 或 -O1 
优化生成代码。 
-O2 
进一步优化。 
-O3 
比 -O2 更进一步优化,包括 inline 函数。 
-shared 
生成共享目标文件。通常用在建立共享库时。 
-static 
禁止使用共享连接。 
-UMACRO 
取消对 MACRO 宏的定义。 
-w 
不生成任何警告信息。 
-Wall 
生成所有警告信息。 
 
gcc 的其他例子,可参阅 info 帮助或 gcc 手册页。作为例子,请看如下的 gcc 命 
令: 
$ gcc -o test -D_DEBUG -g -I../include/ -L../lib -lmylib test.c 
上面的命令编译连接并生成可执行文件 test。-D选项定义了一个 _DEBUG 宏,-g指定生 
成调试信息,-I和 -L 分别指定了一个额外的头文件路径和一个额外的函数库路径,-l 
指定了一个连接时的搜索函数库。 
13.3.3  GNU 的 make 工具 
在大型的开发项目中,通常有几十到上百个的源文件,如果每次均手工键入 gcc 命 
令进行编译的话,则会非常不方便。因此,人们通常利用 make 工具来自动完成编译工作。 
这些工作包括:如果仅修改了某几个源文件,则只重新编译这几个源文件;如果某个头文 
件被修改了,则重新编译所有包含该头文件的源文件。利用这种自动编译可大大简化开发 
工作,避免不必要的重新编译。 
实际上,make 工具通过一个称为 makefile 的文件来完成并自动维护编译工作。 
makefile 需要按照某种语法进行编写,其中说明了如何编译各个源文件并连接生成可执 
行文件,并定义了源文件之间的依赖关系。当修改了其中某个源文件时,如果其他源文件 
依赖于该文件,则也要重新编译所有依赖该文件的源文件。makefile 文件是许多编译器, 
包括 Windows NT 下的编译器维护编译信息的常用方法,只是在集成开发环境中,用户通 
过友好的界面修改 makefile 文件而已。 
默认情况下,GNU make 工具在当前工作目录中按如下顺序搜索 makefile: 
? GNUmakefile 
? makefile 
? Makefile 
在 UNIX 系统中,习惯使用 Makefile 作为 makfile 文件。如果要使用其他文件作 
为 makefile,则可利用类似下面的 make 命令选项指定 makefile 文件: 
$ make -f Makefile.debug 
13.3.3.1  makefile 的内容 
makefile 中一般包含如下内容: 
? 需要由 make 工具创建的项目,通常是目标文件和可执行文件。通常使用“目 
标(target)”一词来表示要创建的项目。 
? 要创建的项目依赖于哪些文件。 
? 创建每个项目时需要运行的命令。 
例如,假设你现在有一个 C++ 源文件 test.C,该源文件包含有自定义的头文件  
test.h,则目标文件 test.o 明确依赖于两个源文件:test.C 和 test.h。另外,你可能 
只希望利用 g++ 命令来生成 test.o 目标文件。这时,就可以利用如下的 makefile 来 
定义 test.o 的创建规则: 
# This makefile just is a example. 
# The following lines indicate how test.o depends 
# test.C and test.h, and how to create test.o 
 
test.o: test.C test.h 
    g++ -c -g test.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 文件中可定义多个目标,利用 make target 命令可指定要编译的目 
标,如果不指定目标,则使用第一个目标。通常,makefile 中定义有 clean 目标,可用 
来清除编译过程中的中间文件,例如: 
clean: 
    rm -f *.o 
运行 make clean 时,将执行 rm -f *.o 命令,最终删除所有编译过程中产生的所有中 
间文件。 
13.3.3.2  makefile 中的变量(宏) 
GNU 的 make 工具除提供有建立目标的基本功能之外,还有许多便于表达依赖性关系 
以及建立目标的命令的特色。其中之一就是变量或宏的定义能力。如果你要以相同的编译 
选项同时编译十几个 C 源文件,而为每个目标的编译指定冗长的编译选项的话,将是非 
常乏味的。但利用简单的变量定义,可避免这种乏味的工作: 
# Define macros for name of compiler 
CC = gcc 
 
# Define a macr o for the CC flags 
CCFLAGS = -D_DEBUG -g -m486 
 
# A rule for building a object file 
test.o: test.c test.h 
    $(CC) -c $(CCFLAGS) test.c 
在上面的例子中,CC 和 CCFLAGS 就是 make 的变量。GNU make 通常称之为变量, 
而其他 UNIX 的 make 工具称之为宏,实际是同一个东西。在 makefile 中引用变量的值 
时,只需变量名之前添加 $ 符号,如上面的 $(CC) 和 $(CCFLAGS)。 
GNU make 有许多预定义的变量,这些变量具有特殊的含义,可在规则中使用。表 13-2  
给出了一些主要的预定义变量,除这些变量外,GNU make 还将所有的环境变量作为自己 
的预定义变量。 
表 13-2  GNU make 的主要预定义变量 
预定义变量 
含义 
$* 
不包含扩展名的目标文件名称。 
$+ 
所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。 
$< 
第一个依赖文件的名称。 
$? 
所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。 
$@ 
目标的完整名称。 
$^ 
所有的依赖文件,以空格分开,不包含重复的依赖文件。 
$% 
如果目标是归档成员,则该变量表示目标的归档成员名称。例如,如果目标名称为  
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  隐含规则 
GNU make 包含有一些内置的或隐含的规则,这些规则定义了如何从不同的依赖文件 
建立特定类型的目标。GNU make 支持两种类型的隐含规则: 
? 后缀规则(Suffix Rule)。后缀规则是定义隐含规则的老风格方法。后缀规则 
定义了将一个具有某个后缀的文件(例如,.c 文件)转换为具有另外一种后缀 
的文件(例如,.o 文件)的方法。每个后缀规则以两个成对出现的后缀名定义, 
例如,将 .c 文件转换为 .o 文件的后缀规则可定义为: 
.c.o: 
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< 
? 模式规则(pattern rules)。这种规则更加通用,因为可以利用模式规则定义 
更加复杂的依赖性规则。模式规则看起来非常类似于正则规则,但在目标名称 
的前面多了一个 % 号,同时可用来定义目标和依赖文件之间的关系,例如下面 
的模式规则定义了如何将任意一个 X.c 文件转换为 X.o 文件: 
%.c:%.o 
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< 
13.3.3.4  makefile 实例 
下面给出一个 makefile 的实例,见清单 13-3。 
清单 13-3  一个 makfile 文件的例子 
――――――――――――――――――――――――――――――――――――――― 
#---------------------------------------------------------------------- 
# Makefile for test programs of MiniGUI4 for Linux. 
# Copyright (c) 1999, Wei Yongming. 

#---------------------------------------------------------------------- 
 
# These may be changed, if desired 
CC=             gcc 
CCOPTS=         -O2 -Wall -m486 -D_REENTRANT 
 
# ------------------------------------------------------------------- # 
 
CFLAGS= $(CCOPTS) 
TESTPROG=       testgui 
TESTOBJ =       testgui.o misc.o gdi.o cursor.o event.o desktop.o / 
                  window.o main.o cliprect.o menu.o fixstr.o timer.o dti.o 
 
all:    $(TESTPROG) 
 
$(TESTPROG):    $(TESTOBJ) 
        $(CC) -o $(TESTPROG) $(TESTOBJ) -lvgagl -lvga -lpthread 
 
clean: 
        rm -f *.o *~ .*.swp .*.swo 
 
testgui.o:      window.h event.h misc.h gdi.h cursor.h main.h common.h 
cliprect.o:    cliprect.h common.h 
misc.o:         misc.h gdi.h cliprect.h common.h 
gdi.o:          gdi.h cliprect.h misc.h common.h 
cursor.o:       cursor.h gdi.h cliprect.h misc.h common.h 
event.o:        event.h cursor.h misc.h common.h 
desktop.o:      menu.h fixstr.h cliprect.h window.h event.h cursor.h / 
                 gdi.h misc.h common.h 
window.o:       menu.h fixstr.h cliprect.h window.h event.h cursor.h / 
                 gdi.h misc.h common.h 
menu.o:         menu.h fixstr.h cliprect.h window.h event.h cursor.h / 
                 gdi.h misc.h common.h 
fixstr.o:       fixstr.h common.h 
timer.o:        timer.h window.h gdi.h cliprect.h inline.h common.h 
dti.o:          menu.h window.h gdi.h cliprect.h inline.h common.h 
main.o:         main.h common.h 
――――――――――――――――――――――――――――――――――――――― 
在上面的 makefile 文件中,定义了多个目标以及和其他源文件的依赖关系。我们可 
以注意到上述 makefile 中定义了一些变量,同时使用 GNU make 的内置隐含规则编译各 
个 .c 源文件。执行 make 命令时的输出如下: 
[WeiYM@rocket src]$ make clean 
rm -f *.o *~ .*.swp .*.swo 
[WeiYM@rocket src]$ make 
gcc -O2 -Wall -m486 -D_REENTRANT   -c testgui.c -o testgui.o 
gcc -O2 -Wall -m486 -D_REENTRANT   -c misc.c -o misc.o 
gcc -O2 -Wall -m486 -D_REENTRANT   -c gdi.c -o gdi.o 
gcc -O2 -Wall -m486 -D_REENTRANT   -c cursor.c -o cursor.o 
gcc -O2 -Wall -m486 -D_REENTRANT   -c event.c -o event.o 
gcc -O2 -Wall -m486 -D_REENTRANT   -c desktop.c -o desktop.o 
gcc -O2 -Wall -m486 -D_REENTRANT   -c window.c -o window.o 
gcc -O2 -Wall -m486 -D_REENTRANT   -c main.c -o main.o 
gcc -O2 -Wall -m486 -D_REENTRANT   -c cliprect.c -o cliprect.o 
gcc -O2 -Wall -m486 -D_REENTRANT   -c menu.c -o menu.o 
gcc -O2 -Wall -m486 -D_REENTRANT   -c fixstr.c -o fixstr.o 
gcc -O2 -Wall -m486 -D_REENTRANT   -c timer.c -o timer.o 
gcc -O2 -Wall -m486 -D_REENTRANT   -c dti.c -o dti.o 
gcc -o testgui testgui.o misc.o gdi.o cursor.o event.o desktop.o window.o main.d 
13.3.3.5  make 命令的常用选项 
我们知道,直接在 make 命令的后面键入目标名可建立指定的目标,如果直接运行  
make,则建立第一个目标。我们还知道可以用 make -f mymakefile 这样的命令指定 make  
使用特定的 makefile,而不是默认的 GNUmakefile、makefile 或 Makefile。但 GNU make  
命令还有一些其他选项,表 13-3 给出了这些选项。 
表 13-3  GNU make 命令的常用命令行选项 
命令行选项 
含义 
-C DIR 
在读取 makefile 之前改变到指定的目录 DIR。 
-f FILE 
以指定的 FILE 文件作为 makefile。 
-h 
显示所有的 make 选项。 
-i 
忽略所有的命令执行错误。 
-I DIR 
当包含其他 makefile 文件时,可利用该选项指定搜索目录。 
-n 
只打印要执行的命令,但不执行这些命令。 
-p 
显示 make 变量数据库和隐含规则。 
-s 
在执行命令时不显示命令。 
-w 
在处理 makefile 之前和之后,显示工作目录。 
-W FILE 
假定文件 FILE 已经被修改。 
 
13.3.4  GNU 的调试器 
GNU 的调试器称为 gdb,该程序是一个交互式工具,工作在字符模式。在 X Window 系 
统中,有一个 gdb 的前端图形工具,称为 xxgdb。gdb 是功能强大的调试程序,可完成 
如下的调试任务: 
? 设置断点; 
? 监视程序变量的值; 
? 程序的单步执行; 
? 修改变量的值。 
在可以使用 gdb 调试程序之前,必须使用 -g 选项编译源文件。可在 makefile 中 
如下定义 CFLAGS 变量: 
CFLAGS = -g 
13.3.4.1  运行 gdb 
运行 gdb 调试程序时通常使用如下的命令: 
gdb progname 
执行上述命令之后,gdb 将显示一个命令提示行: 
[WeiYM@rocket src]$ gdb testgui 
GNU gdb 4.17.0.4 with Linux/x86 hardware watchpoint and FPU support 
Copyright 1998 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 the conditions. 
There is absolutely no warranty for GDB.  Type "show warranty" for details. 
This GDB was configured as "i386-redhat-linux"... 
(no debugging symbols found)... 
(gdb) 
(gdb) 就是 gdb 的提示符。在 gdb 提示符处键入help,将列出命令的分类,主要的分 
类有: 
? aliases:命令别名 
? breakpoints:断点定义; 
? data:数据查看; 
? files:指定并查看文件; 
? internals:维护命令; 
? running:程序执行; 
? stack:调用栈查看; 
? statu:状态查看; 
? tracepoints:跟踪程序执行。 
键入 help 后跟命令的分类名,可获得该类命令的详细清单。例如,键入 help stack, 
将得到如下的命令清单: 
(gdb) help stack 
Examining the stack. 
The stack is made up of stack frames.  Gdb assigns numbers to stack frames 
counting from zero for the innermost (currently executing) frame. 
 
(lines deleted) 
 
List of commands: 
 
backtrace -- Print backtrace of all stack frames 
bt -- Print backtrace of all stack frames 
down -- Select and print stack frame called by this one 
frame -- Select and print a stack frame 
return -- Make selected stack frame return to its caller 
select-frame -- Select a stack frame without printing anything 
up -- Select and print stack frame that called this one 
要退出 gdb 时,只需在提示符处键入 q 并按 Enter 键。 
表 13-4  列出了常用的 gdb 命令。 
表 13-4  常用的 gdb 命令 
命令 
解释 
break NUM 
在指定的行上设置断点。 
bt 
显示所有的调用栈帧。该命令可用来显示函数的调用顺序。 
clear 
删除设置在特定源文件、特定行上的断点。其用法为:clear FILENAME:NUM。 
continue 
继续执行正在调试的程序。该命令用在程序由于处理信号或断点而导致停止运行时。 
display EXPR 
每次程序停止后显示表达式的值。表达式由程序定义的变量组成。 
file FILE 
装载指定的可执行文件进行调试。 
help NAME 
显示指定命令的帮助信息。 
info break 
显示当前断点清单,包括到达断点处的次数等。 
info files 
显示被调试文件的详细信息。 
info func 
显示所有的函数名称。 
info local 
显示当函数中的局部变量信息。 
info prog 
显示被调试程序的执行状态。 
info var 
显示所有的全局和静态变量名称。 
kill 
终止正被调试的程序。 
list 
显示源代码段。 
make 
在不退出 gdb 的情况下运行 make 工具。 
next 
在不单步执行进入其他函数的情况下,向前执行一行源代码。 
print EXPR 
显示表达式 EXPR 的值。 
quit 
退出 gdb。 
set variable 
设置变量的值,语法为 set variable VAR=VALUE。 
shell CMD 
在不退出 gdb 的情况下执行 Shell 命令。 
step 
向前执行一行源代码,如果可能,跳入调用函数执行。 
watch VAR 
当变量 VAR 的值发生变化时,显示该变量的值。 
where 
显示调用序列。根据该命令的输出,可查找程序出问题的地方。 
x/F ADDR 
检查内存地址为 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> 
 
static char buff [256]; 
static char* string; 
int main () 

 
    printf ("Please input a string: "); 
    gets (string); 
 
    printf ("/nYour string is: %s/n", string); 

――――――――――――――――――――――――――――――――――――――― 
上面这个程序非常简单,其目的是接受用户的输入,然后将用户的输入打印出来。该 
程序使用了一个未经过初始化的字符串地址 string,因此,编译并运行之后,将出现  
Segment Fault 错误: 
[WeiYM@versa gcc]$ gcc -o test -g test.c 
[WeiYM@versa gcc]$ ./test 
Please input a string: asfd 
Segmentation fault (core dumped) 
为了查找该程序中出现的问题,我们利用 gdb,并按如下的步骤进行: 
1.运行 gdb test 命令,装入 test 可执行文件; 
2.执行装入的 test 命令: 
(gdb) run 
Starting program: /home/WeiYM/projects/study/gcc/test 
Please input a string: abcd 
 
Program received signal SIGSEGV, Segmentation fault. 
0x4006a7da in _IO_gets (buf=0x0) at iogets.c:55 
iogets.c:55: No such file or directory. 
3.使用 where 命令查看程序出错的地方: 
 (gdb) where 
#0  0x4006a7da in _IO_gets (buf=0x0) at iogets.c:55 
#1  0x8048413 in main () at test.c:11 
#2  0x40030cb3 in __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) list test.c:11 

7       int main () 
8       { 

10          printf ("Please input a string: "); 
11          gets (string); 
12 
13          printf ("/nYour string is: %s/n", string); 
14      } 
15 
5.唯一能够导致 gets 函数出错的因素就是变量 string。用 print 命令查看 string  
的值: 
(gdb) print string 
$1 = 0x0 
显然,为 gets 函数传递了一个空指针,这就是程序的错误所在。这是因为 string 是 
一个全局变量,运行时初始化为 0,即空指针。 
6.在 gdb 中,我们可以直接修改变量的值,只要将 string 取一个合法的指针值就 
可以了,为此,我们在第 11 行处设置断点: 
(gdb) break 11 
Breakpoint 1 at 0x8048408: file test.c, line 11. 
(gdb) run 
The program being debugged has been started already. 
Start it from the beginning? (y or n) y 
Starting program: /home/WeiYM/projects/study/gcc/test 
 
Breakpoint 1, main () at test.c:11 
11          gets (string); 
7.程序重新运行到第 11 行处停止,这时,我们可以用 set variable 命令修改 string  
的取值: 
(gdb) set var string=buff 
8.然后继续运行,将看到正确的程序运行结果: 
(gdb) cont 
Continuing. 
Please input a string: abcd 
 
Your string is: abcd 
 
Program exited with code 026. 
上面的例子足以说明 gdb 的一般使用方法,当然,gdb 的命令很多,还可用来调试 
多线程程序。这些命令的使用,需要读者在实践中学习并掌握。 
13.3.5  版本控制工具 
在小型软件的开发过程中,利用 make 工具可自动完成一些编译工作。如果涉及到的 
开发人员不多,代码的维护是简单的。人们通常利用备份和强制性的代码注释来维护源程 
序的修改。但在大型项目中,开发人员和代码规模非常大的情况下,就有必要采用专门的 
版本控制软件来维护代码的修订。在 Microsoft 的开发工具中,有一个称为 SourceSafe 的 
软件,专门用来进行版本控制,在 PowerBuilder 这样的大型开发工具中,也通常有内置 
的版本控制系统。在 Linux 上,通常使用的版本控制系统叫 RCS,即 Revision Control  
System(修订控制系统)。 
利用版本控制系统,开发人员能够在需要时恢复某个文件的特定版本或修订。在使用 
版本控制系统时,开发人员对一个文件的修改工作按如下的步骤进行: 
1.当开发人员有一个初始的源文件版本时,将该文件归档到版本控制系统中。 
2.当开发人员需要对某个文件进行修改时,首先要获得当前版本的一个拷贝。在某 
个开发人员获得该文件的一个拷贝时,版本控制系统将确保其他人不能获得该文件。 
3.当修改完源文件,并经过测试之后,开发人员将该文件保存为一个新的版本。 
4.其后需要修改该文件时,将始终在该文件的最新版本的基础上进行修改。 
为此,RCS 提供了许多工具帮助维护源代码的修订,主要的工具见表 13-5。 
表 13-5  RCS 的主要工具 
工具名称 
功能 
ci 
建立某个文件的新修订或将某个工作文件添加到 RCS 文件中。 
co 
为只读目的获取某个文件的一个工作版本。(co -l 可提供一个工作文件并锁定原有 
的文件,这样就可以修改工作文件。) 
ident 
在文件中搜索标识符。 
merge 
将两个文件合并成为第三个文件。 
rcsdiff 
就工作文件和 RCS 维护的文件进行比较。 
rcsmerge 
合并某个文件的不同版本。 
rlog 
查看某个文件的修改日志。 
 
限于篇幅不能详细讲解 RCS 的使用,详细信息可参阅 rcsintro(1)手册页以及相 
关帮助信息。 
13.3.6  Perl 简介 
Linux 为开发人员提供了广阔的活动舞台。开发人员不仅能够利用 C/C++、Fortran、 
Pacal、Lisp 等多种编程语言编写程序,而且能够使用各种脚本语言编写程序,其中包括  
Shell 脚本、Perl 脚本、Tcl/Tk 脚本等。通常说来,脚本语言就是一种编程语言,但它 
更加贴近自然语言,具有简单、方便的特点,并且在某些方面,比起传统的编程语言来说 
更加有优势。 
本小节将简单描述在 Linux 中广为使用的脚本语言 Perl。Perl,即 Practical  
Extraction Report Language(实用析取报表语言),由 Larry Wall 创立,可用来从文 
本文件中析取信息,并利用这些信息建立报表。由于 Perl 和 Linux 一样,也是自由软 
件,因此,Perl 在各种计算机系统中广为流传。各种 Linux 商业发行版本中均包含有  
Perl。 
13.3.6.1  Perl 脚本的执行 
和传统的编译语言不同,Perl 脚本一般是一些文本文件,由 perl 程序解释并执行  
Perl 脚本。例如,下面是一个简单的 Perl 脚本: 
#!/usr/bin/perl 
# This is a comment. 
print "Hello, World!/n" 
该脚本的第一行利用 #! 指定了脚本的解释程序为 /usr/bin/perl,第二行是一个注 
释行,第三行打印“Hello, World!”。 
执行该脚本之前,首先要将脚本文件设置为可执行模式。如果脚本名称为 hello.pl 
(.pl 一般是 Perl 脚本的后缀名),则使用如下的命令: 
$ chmod +x hellol.pl 
然后可在命令行直接执行 hello.pl: 
$ ./hello.pl 
实际上,Linux 将按如下命令执行脚本: 
/usr/bin/perl ./hello.pl 
13.3.6.2  Perl 的基本语法 
Perl 包含有与其他语言一样的共同特色: 
? 用来保存不同类型数据的变量。 
? 利用运算符将变量组合而成的表达式。 
? 执行动作的表达式。 
? 可控制语句执行路线的流控制语句。 
? 划分功能,提供可重用性的函数(子例程或例程)。 
Perl 程序的书写形式是自由的,和 C 类似,每个 Perl 语句以分号(;)结束,# 代 
表一个注释行,Perl 也用与 C 一样的大括号({...})来定义语句组。 
下面的小节讲述 Perl 变量、运算符和表达式、语句、流控制以及其他的特性。 
13.3.6.3  变量 
在 Perl 程序中,不必显示定义变量,所有的变量以 @、$ 或 % 打头,分别代表了 Perl  
的三种变量类型。 
? 标量型变量(Scalar Variable)。标量型变量定义了基本的数据类型,包括整 
数、浮点数和字符串。标量型变量由 $ 字符开头: 
$title = "Linux And Windows NT"; 
$count = 10; 
? 数组型变量(Array Variable)。数组型变量是标量型变量的集合,数组型变量 
的前缀是 @,如下所示: 
@commands = ("ls", "dir", "cd", "rm", "mv"); 
@length = (2, 3, 2, 2, 2); 
可通过类似 C 语言的下标方法访问数组中的元素,例如: 
$commands[0] 
? 关联型数组(Associate Array)。关联型数组是一些键/值对的集合,其中的键 
是字符串,而值可以是任意一种标量型变量。关联型数组的前缀是 %。利用关 
联型数组,可通过字符串键而快速查找对应的值,如下所示: 
%grades = ("WeiYM", 60, "ZhengX", 100, "other", 50); 
利用 {key} 可针对关联数组中的键进行求值: 
$mygrade = $grads{WeiYM} 
需要注意的是,在语句中,当变量处在双引号("")之中时,Perl 将对变量求值, 
而当变量处在单引号('')中时,Perl 不会对变量求值。下面的两个语句将输出不同的 
内容: 
print "My grade is $grads[WeiYM]/n";    # Output: My grade is 60 
print 'My grade is $grads[WeiYM]/n';    # Output: My grade is $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 ($input eq "quit") { 
        exit; 

其他类似 FORTRAN 的比较运算符有:ne(不相等)、lt(小于)、gt(大于)、le(小于或 
等于)、ge(大于或等于)。也可以使用 cmp 运算符比较字符串,将返回 -1、0、1 等值, 
返回值与 C 函数 strcmp 的返回值类似。 
Perl 提供的其他独有的运算符有: 
? **:指数运算符,如: 
$y **= 2; 
? ():空数组运算符,可用来初始化数组变量,如: 
@commands = (); 
? .:字符串连接运算符,可用来连接两个字符串变量,如: 
$message = $part1.$part2; 
? x=:重复运算符,可利用该运算符重复字符串指定的次数,如: 
$marker = "*"; 
$marker x= 80;    # $maker 将包含 65 个 * 
? ..:范围运算符,可利用该运算符方便地初始化字符串,如: 
$alphabet = ('A'..'Z'); 
在第八章中我们了解了正则表达式。在 Perl 中,正则表达式得到了非常广泛的使用。 
例如: 
if ( $name =~ /[wW]ei[yY][mM]/ ) { 
        print "$name is a bad man./n"; 

将在 $name 取“Weiym”或“weiym”时执行打印语句。注意在上述语句中,正则表达式 
由 // 包围,检查是否匹配时,采用 =~ 运算符(检查不匹配时,用 !~ 运算符)。 
13.3.6.5  流控制语句 
Perl 提供了和 C 语言一样的流控制语句,并且包含一些额外的特色。Perl 的条件 
语句的形式如下: 
conditional-statement { 
        Perl code to execute if conditional is true 

注意,包围执行代码的大括号是必须存在的,这点和 C 语言不同。值得指出的是,Perl  
和 C 一样,将任何非零的值看成是 true,而将零看成是 false。 
表 13-6 简要给出了 Perl 的流控制语句。 
表 13-6  Perl 的流控制语句及其用法 
流控制语句 
说明 
举例 
if 
和 C 语言的 if 语句类似。可使用 elseif  
和 else 分句。 
if ($user eq "root") { 
        print "the super user./n"; 
}else { 
        print "a normal user./n"; 

unless 
和 if 语句一样的使用形式,包括 elseif 和  
else 分句。其判断结果和 if 相反。 
unless ($user eq "root") { 
        print "a normal user./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 $_; 
        goto ReDo; 

 
13.3.6.6  访问系统 
Perl 可通过如下途径执行 Linux 命令: 
? 调用 system,传递要执行的 Linux 命令。 
? 在一对反引号(`)中指定 Linux 命令。 
? 调用 fork 函数,类似 Linux 的 fork 系统调用。将复制当前脚本并在子进程 
中处理新的命令。 
? 调用 exec 函数,类似 Linux 的 exec 系统调用。将以新脚本或命令覆盖当前 
脚本。 
? 使用 fork 和 exec 来提供类似 Shell 的功能(通过子进程监视用户的输入并 
处理用户命令)。 
我们举一个例子说明了上述最后一种命令执行途径,见清单 13-5。 
清单 13-5  psh.pl――使用 fork 和 exec 来执行用户命令 
――――――――――――――――――――――――――――――――――――――― 
#!/usr/bin/perl 
 
# 该脚本使用 "fork" 和 "exec"  
# 来执行由用户键入的命令 
 
$prompt = "Command (/"exit/" to quit): "; 
print $prompt; 
 
while (<STDIN>) { 
        chop;          # 清除尾部的 /n 字符 
        if ($_ eq "exit") {exit 0;} 
 
        $status = fork; 
        if ($status) { 
                # 这是父进程,等待子进程执行结束 
                wait; 
                print $prompt; 
                next; 
        } else { 
                exec $_; 
        } 

――――――――――――――――――――――――――――――――――――――― 
执行上述脚本,将得到如下结果: 
[WeiYM@versa perl]$ ./psh.pl 
Command ("exit" to quit): ps 
  PID TTY          TIME CMD 
  670 pts/1    00:00:00 bash 
  807 pts/1    00:00:00 psh.pl 
  808 pts/1    00:00:00 ps 
Command ("exit" to quit): exit 
13.3.6.7  文件处理 
从前面的例子中可以看到,Perl 使用 STDIN 作为标准输入的标识符。在 Perl 中, 
文件是由句柄代表的,通常用大写表示。除 STDIN 之外,Perl 中还有两个预定义的文件 
句柄,即 STDOUT 和 STDERR,分别代表标准输出设备和错误输出设备。 
如果要使用其他文件,则可以利用 open 函数打开文件,用 close 函数关闭文件, 
而用 <FILEHANDLE> 来从一个文件中读取一行。例如: 
open (PWDFILE, "/etc/passwd"); 
while (<PWDFILE>) { print $_;} 
close PWDFILE; 
13.3.6.8  用户自定义例程 
Perl 中包含有非常多的内置函数,但用户也可以自定义例程。在 Perl 脚本中使用 
例程非常简单: 
#!/usr/bin/perl 
sub display 

        # 将函数参数复制到局部变量 
        local ($name, $x) = @_; 
        print "Grade of $name is $x/n"; 

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

        if (x == y) { 
                ... 
        } else if (x > y) { 
                ... 
        } else { 
                ... 
        } 
 
        return 0; 

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