自从有了抽象数据类型的思想指导,突然有种豁然开朗的感觉,感觉这枯燥繁琐的C语言有了生气,大脑思考问题时,增添了不少灵感和思路。面对一个复杂问题,不再迷茫而不知所措。

根据模块化开发的思想,对于一个复杂问题,一般是根据”逐步求精“的指导精神,将问题分析,按功能将程序分解为多个模块,其中包含main函数的称作主模块,其它模块都可看作是主模块的库,甚至有些模块是相对独立的,可以作为多个应用程序的库,以实现最大限度的代码重用。
  而这模块的划分就需要根据经验,尽可能地将在复杂的问题中提取多个抽象数据类型,当然这个类型和它的行为就可通过接口来定义,也就是通过一个头文件形式。有了接口,进而再通过底层的实现和表示形式完成接口中定义的功能。
  有了这种抽象类型的接口和实现,一方面将某功能模型抽象独立出来,实现更大限度的重用。另一方面,使程序模块化,层次化更清晰,从开发效率和维护方面,好处多多,不再详述


  这里我们以扫描器抽象类型为例,扫描器是指将一个字符串分解为标记,其中标记是一连串的字母或数字组成的字符串,或空格或标点组成的单个字符串。这种抽象类型适合很多场合,如对一句话进行词法分析,编译器读取代码语句等场景,这个过程都是必不可少的,有必要将此功能抽象出来,作为相对独立的类型。

代码示例:

 1 /* scanner.h --- 
 2  * 
 3  * Filename: scanner.h
 4  * Description: 扫描器接口
 5  * Author: 
 6  * Maintainer: 
 7  * Created: 二  8月 14 09:04:30 2012 (+0800)
 8  * Version: 
 9  * Last-Updated: 二  8月 14 16:35:16 2012 (+0800)
10  *           By: 
11  *     Update #: 32
12  * URL: 
13  * Keywords: 
14  * Compatibility: 
15  * 
16  */
17 
18 /* Commentary: 
19  * 扫描器功能:将一个字符串分解为标记,其标记是下列之一:
20  *           一连串数值字母字符(即字母或数字)或 由空格或标点组成的单个字符的字符串
21  * 
22  */
23 
24 /* Code: */
25 
26 #include "../commonlib/genlib.h"
27 
28 #ifndef _scanner_h
29 #define _scanner_h
30 
31 typedef struct scannerCDT *scannerADT;
32 
33 /*************************************************************************
34 *功能描述:初始化一个扫描器
35 *参数列表:line:将用来分解的字符串内容
36 *返回类型:
37 **************************************************************************/
38 scannerADT InitScanner(string line);
39 
40 /*************************************************************************
41 *功能描述:获取下一个标记(token)
42 *参数列表:scanner:扫描器实例
43 *返回类型:
44 **************************************************************************/
45 string GetNextToken(scannerADT scanner);
46 
47 /*************************************************************************
48 *功能描述:判断是否到达尾部
49 *参数列表:scanner:扫描器实例
50 *返回类型:
51 **************************************************************************/
52 
53 bool IsEndOfLine(scannerADT scanner);
54 
55 /*************************************************************************
56 *功能描述:释放扫描器实例
57 *参数列表:
58 *返回类型:
59 **************************************************************************/
60 void FreeScanner(scannerADT scanner);
61 
62 #endif
63 
64 /* scanner.h ends here */

 

代码示例:

 

  1 /* scanner.c --- 
  2  * 
  3  * Filename: scanner.c
  4  * Description: 扫描器的实现 
  5  * Author: magc
  6  * Maintainer: 
  7  * Created: 二  8月 14 10:49:45 2012 (+0800)
  8  * Version: 
  9  * Last-Updated: 二  8月 14 16:47:49 2012 (+0800)
 10  *           By: magc
 11  *     Update #: 254
 12  * URL: 
 13  * Keywords: 
 14  * Compatibility: 
 15  * 
 16  */
 17 
 18 /* Commentary: 
 19  * 
 20  * 
 21  * 
 22  */
 23 
 24 /* Code: */
 25 #include <assert.h>
 26 #include <ctype.h>
 27 #include <errno.h>
 28 #include <limits.h>
 29 #include <string.h>
 30 #include <stdarg.h>
 31 #include <stdlib.h>
 32 #include <stdio.h>
 33 #include "../commonlib/genlib.h"
 34 #include "../commonlib/strlib.h"
 35 #include "scanner.h"
 36 
 37 struct scannerCDT{
 38     int pos;                   //当前游标位置,通过此结构属性来实现状态的封闭,减少使用全局变量
 39     string line;               //当前字符串(行)的内容
 40     int line_len;              //当前字符串的总长度
 41 };
 42 /*************************************************************************
 43 *功能描述:初始化一个扫描器实例
 44 *参数列表:line:要分析的字符串
 45 *返回类型:返回一个初始化后的扫描器
 46 **************************************************************************/
 47 scannerADT InitScanner(string line){
 48     scannerADT scanner = New(scannerADT);
 49     scanner->line = CopyString(line);
 50     scanner->pos = 0;                      //通过结构属性,来代替全局变量的封闭状态功能,避免使用全局变量
 51     scanner->line_len = StringLength(line);
 52     
 53     return scanner;
 54 }
 55 
 56 /*************************************************************************
 57 *功能描述:获取下一个标记(token),标点和空格分别作为独立的标记,每一个标记返回一次。
 58 *参数列表:scanner:扫描器
 59 *返回类型:
 60 **************************************************************************/
 61 string GetNextToken(scannerADT scanner ){
 62 
 63     char buf;
 64     
 65     int start = -1;//记录当前这次单词的起始位置
 66     buf = IthChar(scanner->line,scanner->pos);
 67     if(isalnum(buf)){
 68         //若字符是字母或数字,则开始用start标记起始,直到遇到非字母和非数字结束,作为单独的标记
 69         start = scanner->pos;
 70         while(isalnum(buf)){
 71             scanner->pos++;
 72             buf = IthChar(scanner->line,scanner->pos);
 73         }
 74         return SubString(scanner->line,start,scanner->pos-1);
 75     }
 76     else{
 77         //若字符是非字母或非数字,则作为单个字符组成的字符串标记
 78         scanner->pos++;
 79         return CharToString(buf);
 80     }
 81     
 82 }
 83 
 84 /*************************************************************************
 85 *功能描述:判断是否到达行尾部
 86 *参数列表:
 87 *返回类型:
 88 **************************************************************************/
 89 bool IsEndOfLine(scannerADT scanner){
 90     return (scanner->pos >= scanner->line_len-1);
 91     
 92 }
 93 /*************************************************************************
 94 *功能描述:释放扫描器实例
 95 *参数列表:
 96 *返回类型:
 97 **************************************************************************/
 98 void FreeScanner(scannerADT scanner){
 99     FreeBlock(scanner->line);
100     FreeBlock(scanner);
101     
102 }
103 /*************************************************************************
104 *功能描述:主函数
105 *参数列表:
106 *返回类型:
107 **************************************************************************/
108 int main(int argc, char * argv[])
109 {
110     string line = "hello ! how are you !\n tiian jin! ";
111     scannerADT scan = InitScanner(line);
112     int i = 1;
113     
114     while(!IsEndOfLine(scan)){
115         printf("第%d个标记:|%s|\n",i,GetNextToken(scan));
116         i++;
117     }
118 
119     FreeScanner(scan);//释放内存空间
120 }
121 
122 
123 
124 /* scanner.c ends here */

 
注:
1、体会通过接口形式定义扫描器抽象类型的好处
2、体会通过抽象类型的属性来实现状态的封装,避免使用全局变量(使用全局变量的程序很难理解和控制)
3、理解这种接口和实现的职责和界限,接口只限行为,不管底层表示和实现细节,基础同接口,可以有多种实现方式。

 

在GCC下编译运行结果: