《Code Reading》学习笔记(二)

类别:编程语言 点击:0 评论:0 推荐:
此书基本上以开源项目 NetBSD 的源码为例,因为笔者认为在类 Unix 系统中 NetBSD 强调了设计的正确性和代码书写的合理性。
让我们从第二章开始吧,先来看一个完整的程序。
以 Unix 系统中一个简单但是很有用的程序 echo 来看, 此程序会在标准输出设备(屏幕)输出它的参数,常被用来向用户显示如下信息:

echo "Cool! Let 's get to it..."

下面是程序代码,如你所看到的,一半以上的代码表示诸如版权、许可信息和程序版本等法律和管理信息。在一个大型有组织的系统里这样的信息必不可少。

C 和 C++ 要正确使用库函数就需要包含头文件。库文档通常会列出每个函数需要的头文件。

标准 C、C++ 和 Java 程序从函数 main 开始执行。 第一次观察一个程序从 main 下手是一个不错的开始。注意有些操作系统例如Microsoft Windows、Java applet 和 servlet hosts, palmtop PCs, 还有嵌入式系统可能使用另一个函数做为程序的入口点,例如 WinMain 或者 init。

C/C++ 程序中 main 函数的两个参数(通常名为 argc 和 argv)用来从操作系统中传递关于指定命令行参数的信息到程序.argc 变量包含了程序参数的数量,argv 是一个包含所有实际参数的字符串数组(在位置 0 包含了程序名)。argv 数组以一个NULL 元素结束,可以以两种不同的方式来处理参数:根据 argc计数或者通过遍历 argv 并将每个元素和 NULL 作比较。在 Java 程序中你会发现 argv 字符串数组和它的 length 方法用来实现同一个目的,然而在 Perl 代码中同样的架构会是@ARGV 数组和$#ARGV 标量。

Figure 2.1  Unix 中的 echo 程序

/* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include <sys/cdefs.h> <-- a #ifndef lint __COPYRIGHT( "@(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"); __RCSID("$NetBSD: echo.c,v 1.7 1997/07/20 06:07:03 thorpej Exp $"); #endif /* not lint */ #include <stdio.h> <-- b #include <stdlib.h> <-- c #include <string.h> <-- d int main __P((int, char *[])); <-- e int main(argc, argv) int argc; char *argv[]; { int nflag; /* This utility may NOT do getopt(3) option parsing. */ if (*++argv && !strcmp(*argv, "-n") ) { ++argv; nflag = 1; } else nflag = 0; while (*argv) } <-- f (void)printf("%s", *argv); if (*++argv) <-- g putchar(' '); <-- h } if (!nflag) <-- i putchar('\n'); exit(0); <-- j }

注释 (版权和发布许可), 编译器会忽略的内容。

(a) 在可执行文件中会以字符串出现的版权和程序版本标识

包含以下函数的标准函数库头文件:

(b) printf

(c) exit

(d) strcmp

(e) 用来为早期的编译器隐藏参数的函数声明

程序以此函数开始

此程序的参数数量

实际上的参数(以程序名开始,以 NULL 结束)

此参数为真时输出不会以换行结束

第一个参数是 -n

跳过参数并设置 nflag

(f) 存在要处理的参数

输出参数

(g) 还有下一个参数吗? (前进到 argv 的下一个位置)

(h) 输出用来分隔的空格

(i) 如果 -n 不给定的话以换行结束输出 

(j) 表示成功退出程序

 

此例中 argc 和 argv 的声明有点不常见。C/C++ 中典型的 main 的声明是:  

int main(int argc, char **argv)

相应的 Java 类方法定义是

public static void main(String args[]) {

当你观察一个命令行程序时你会发现处理参数的方法要么使用手工代码要么,在 POSIX 环境中,使用getopt 函数。Java 程序为了同样的目的可能会使用 GNU gnu.getopt 包。

http://www.gnu.org/software/java/packages.html

echo 命令的标准定义并不适用于 getopt 行为; 因此,指定输出不以换行结束的单-n 参数通过手工代码来处理。 前进到 argv 中 echo 第一个参数的位置 (要记得 0 位置保存的是程序名) 并检验是否有此参数存在以开始比较。 此时 strcmp 才被调用将参数和 Null 对比。 先检查参数是否有效,然后使用此参数,融合布尔 AND (&&) 操作符的使用是一种常见的用法。 因为如果左边的运算式是错误的&& 操作符就不会再去计算右边的计算式。如果调用strcmp或者其它的字符串函数,并传给它一个NULL 值而不是指向真实字符数据的指针的话,在许多操作环境中会导致程序崩溃。

注意strcmp 用来比较字符串相等性时返回的非直观值。相等时返回 0,C语言中的 false 值。因此你会看到很多 C 会定义一个宏 STREQ ,当两字符串相等时用来返回 true。

#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)

幸运的是 Java 中 equals 方法的结果直观的多:

if (isConfig) {
    configFile = args[i];
    isConfig = false;
} else if (args[i].equals("-config")) {
    isConfig = true;
} else if (args[i].equals("-debug")) {
    debug = true;
} else if (args[i].equals("-nonaming")) {

同时如上表示,读代码时可以将一个级联的 if-else if-...-else 序列做为一个互相排斥的几选一来看。

程序中循环访问所有剩下的参数并将它们以空格字符分隔输出的部分相对来说比较直白。

以换行结束输出后,echo 调用表示成功(0)的 exit 来结束程序。

需要此书的朋友可以留下E-mail,英文版,1,98M

本文地址:http://com.8s8s.com/it/it23362.htm