在读了《程序设计抽象思想-C语言描述》第八章后,对此思想感触颇深,而且从中还看到了面向对象编程的影子,不禁感叹这些老外的近二十年前的书的经典,感觉为自己灵活,高效驾驭这古老而又现代的C语言指明的方向,废话少说,步入正题。
 ADT相关知识这里简略列了一个图表,以供浏览:
 

       这里我们参考此书中的例子,仍然以一个简单的计算器程序为例,即后缀式四则运算计算器。我们运用抽象数据类型,为堆栈定义一个ADT,通过stack.h来定义了堆栈类型的行为和ADT名字,然后通过stack.c实现这个接口,并用实际结构表示出stackADT的底层。这样有了堆栈模型,再实现一个计算器那将是随手拈来,虽然例子简单之至,但它的结果,揭示了此种思想带来的好处,在这古老而又底层的C语言之下,确保了程序的层次化和模块化,使程序既易理解,易维护。
  代码示例:

  1 /* stack.h --- 
  2  * 
  3  * Filename: stack.h
  4  * Description:抽象数据类型ADT--堆栈 
  5  * Author: magc
  6  * Maintainer: 
  7  * Created: 四  8月  9 08:59:34 2012 (+0800)
  8  * Version: 
  9  * Last-Updated: 四  8月  9 17:04:55 2012 (+0800)
 10  *           By: magc
 11  *     Update #: 52
 12  * URL: 
 13  * Keywords: 
 14  * Compatibility: 
 15  * 
 16  */
 17 
 18 /* Commentary: 
 19  * ADT 接口
 20  * 只定义行为,而不限制底层表示和实现
 21  * 
 22  */
 23 
 24 /* Change Log:
 25  * 
 26  * 
 27  */
 28 
 29 /* Code: */
 30 #ifndef _stack_h
 31 #define _stack_h
 32 #include "../commonlib/genlib.h"
 33 
 34     
 35 
 36 typedef struct stackCDT *stackADT;
 37 
 38 typedef int stackElement;
 39 
 40 /*************************************************************************
 41 *功能描述:创建新堆栈
 42 *参数列表:
 43 *返回类型:堆栈指针
 44 **************************************************************************/
 45 stackADT NewStack(void);
 46 
 47 /*************************************************************************
 48 *功能描述:释放堆栈内存空间
 49 *参数列表:
 50 *返回类型:
 51 **************************************************************************/
 52 void FreeStack(stackADT stack);
 53 
 54 /*************************************************************************
 55 *功能描述:压栈
 56 *参数列表:
 57 *返回类型:
 58 **************************************************************************/
 59 void Push(stackADT stack ,stackElement element );
 60 
 61 /*************************************************************************
 62 *功能描述:出栈
 63 *参数列表:
 64 *返回类型:弹出的元素
 65 **************************************************************************/
 66 stackElement Pop(stackADT stack);
 67 
 68 /*************************************************************************
 69 *功能描述:判断当前栈是否为空
 70 *参数列表:
 71 *返回类型:TRUE or FALSE
 72 **************************************************************************/
 73 bool StackIsEmpty(stackADT stack );
 74 
 75 /*************************************************************************
 76 *功能描述:判断当前栈是否已满
 77 *参数列表:
 78 *返回类型:
 79 **************************************************************************/
 80 bool StackIsFull(stackADT stack);
 81 
 82 /*************************************************************************
 83 *功能描述:获取当前栈的深度
 84 *参数列表:
 85 *返回类型:
 86 **************************************************************************/
 87 int StackDepth(stackADT stack);
 88 
 89 /*************************************************************************
 90 *功能描述:读取堆栈顶端元素值
 91 *参数列表:
 92 *返回类型:
 93 **************************************************************************/
 94 int StackTop(stackADT stack);
 95 
 96 /*************************************************************************
 97 *功能描述:打印堆栈元素的内容
 98 *参数列表:
 99 *返回类型:
100 **************************************************************************/
101 void StackPrint(stackADT stack);
102 
103 #endif
104 /* stack.h ends here */

堆栈ADT的实现代码:

  1 /* stack.c --- 
  2  * 
  3  * Filename: stack.c
  4  * Description:堆栈ADT的实现 
  5  * Author: magc
  6  * Maintainer: 
  7  * Created: 四  8月  9 10:31:03 2012 (+0800)
  8  * Version: 
  9  * Last-Updated: 四  8月  9 17:12:57 2012 (+0800)
 10  *           By: magc
 11  *     Update #: 108
 12  * URL: 
 13  * Keywords: 
 14  * Compatibility: 
 15  * 
 16  */
 17 
 18 /* Commentary: 
 19  * 
 20  * 
 21  * 
 22  */
 23 
 24 /* Change Log:
 25  * 
 26  * 
 27  */
 28 
 29 /* Code: */
 30 #include <assert.h>
 31 #include <ctype.h>
 32 #include <errno.h>
 33 #include <limits.h>
 34 #include <string.h>
 35 #include <stdarg.h>
 36 #include <stdlib.h>
 37 #include <stdio.h>
 38 #include "stack.h"
 39 #define MaxStackSize 100
 40 
 41 struct stackCDT {
 42     stackElement elements[MaxStackSize];
 43     int count;
 44 };
 45 
 46 
 47 stackADT NewStack(void){
 48     stackADT stack = New(stackADT);
 49     stack->count = 0;
 50     //    stack->elements
 51     return stack;
 52 }
 53 
 54 void FreeStack(stackADT stack){
 55     FreeBlock(stack);
 56     
 57 }
 58 
 59 void Push(stackADT stack , stackElement element){
 60     if(StackIsFull(stack)) Error("the stack is full!\n");
 61     stack->elements[stack->count++] = element;
 62 }
 63 
 64 stackElement Pop(stackADT stack ){
 65     if(StackIsEmpty(stack)) Error("the stack is empty!\n");
 66     
 67     return stack->elements[--stack->count];
 68 }
 69 
 70 bool StackIsFull(stackADT stack){
 71     return (stack->count == MaxStackSize);
 72     
 73 }
 74 bool StackIsEmpty(stackADT stack){
 75     return (stack->count == 0);
 76     
 77 }
 78 int StackDepth(stackADT stack){
 79     return (stack->count);
 80     
 81 }
 82 
 83 int StackTop(stackADT stack){
 84     return (stack->elements[stack->count-1]);
 85 
 86 }
 87 
 88 void StackPrint(stackADT stack){
 89 
 90     if(StackIsEmpty(stack)) Error("the stack is empty !\n");
 91     int i;
 92     printf("the elements of stack is :\n");
 93     
 94     for (i = (stack->count-1); i > -1; i--) {
 95         printf("%d,",stack->elements[i]);
 96         
 97     }
 98 }
 99 
100 /* stack.c ends here */

注:


1)在stack.h只定义了堆栈抽象类型的名字,而它的实际结果交给stack.c来实现和定义(stackCDT在stack.h中叫不完全类型),所以这里定义了stackCDT的结构,但这并不影响stack.h对堆栈的行为定义。
2)在这个实现中采用了数组形式保存堆栈中的元素。同样也可以通过链表形式来实现对堆栈元素的存储,这就是stack.h接口带来的好处,只看重行为,而不管底层实现,将这个工作交给接口的实现者,同时又对接口的调用者来说可以屏蔽,

 

计算器的接口:

 1 /* SimpleCal.h --- 
 2  * 
 3  * Filename: SimpleCal.h
 4  * Description: 简单计算器的接口
 5  * Author: magc
 6  * Maintainer: 
 7  * Created: 四  8月  9 16:53:19 2012 (+0800)
 8  * Version: 
 9  * Last-Updated: 五  8月 10 11:17:30 2012 (+0800)
10  *           By: magc
11  *     Update #: 18
12  * URL: 
13  * Keywords: 
14  * Compatibility: 
15  * 
16  */
17 
18 /* Code: */
19 #include "../commonlib/genlib.h"
20 
21 void CalRun();
22 
23 int CalOperation(stackADT stack ,char oper );
24 
25 
26 
27 
28 /* SimpleCal.h ends here */

计算器的实现:

  1 /* SimpleCal.c --- 
  2  * 
  3  * Filename: SimpleCal.c
  4  * Description: 简单后缀式四则运算计算器
  5  * Author: magc
  6  * Maintainer: 
  7  * Created: 四  8月  9 17:21:42 2012 (+0800)
  8  * Version: 
  9  * Last-Updated: 五  8月 10 10:19:14 2012 (+0800)
 10  *           By: magc
 11  *     Update #: 118
 12  * URL: 
 13  * Keywords: 
 14  * Compatibility: 
 15  * 
 16  */
 17 
 18 /* Commentary: 
 19  * 后缀式四则运算即先输入第一个操作数,再输入第二个操作数,最后输入运算符号,然后输出结果
 20  * 原理:借用堆栈的模型,通过实例化堆栈抽象类型,实现操作数的暂存,此程序虽然实际意义不大,但其结构值得参考
 21  * 
 22  */
 23 
 24 /* Change Log:
 25  * 
 26  * 
 27  */
 28 
 29 /* Code: */
 30 #include <assert.h>
 31 #include <ctype.h>
 32 #include <errno.h>
 33 #include <limits.h>
 34 #include <string.h>
 35 #include <stdarg.h>
 36 #include <stdlib.h>
 37 #include <stdio.h>
 38 #include "../commonlib/genlib.h"
 39 #include "stack.h"
 40 #include "SimpleCal.h"
 41 
 42 #define TRUE 1
 43 
 44 
 45 int main(int argc, char * argv[])
 46 {
 47     CalRun();
 48     return 0;
 49 }
 50 /*************************************************************************
 51 *功能描述:启动运算进程
 52 *参数列表:
 53 *返回类型:
 54 **************************************************************************/
 55 void CalRun(){
 56     stackADT stack = NewStack();
 57         
 58     printf("计算器成功启动。\n");
 59     stackElement num1,num2;
 60     char tag;
 61     
 62     while(TRUE){
 63         printf("请输入第一个操作数:\n");
 64         scanf("%d",&num1);
 65         Push(stack,num1);
 66         
 67         printf("请输入第二个操作数:\n");
 68         scanf("%d",&num2);
 69         Push(stack,num2);
 70         
 71         printf("请选择操作符(+ - * /): \n");
 72         scanf("%s",&tag);
 73         int res = CalOperation(stack,tag);
 74         printf("运算结果是%d\n",res);
 75 
 76         printf("继续计算请输入c,退出请输入其它字符\n");
 77         scanf("%s",&tag);
 78         if(tag != 'c')return;
 79     }
 80     
 81 }
 82 /*************************************************************************
 83 *功能描述:运算函数
 84 *参数列表:
 85 *返回类型:
 86 **************************************************************************/
 87 int CalOperation(stackADT stack,char oper){
 88 
 89     int n1,n2,res = 0;
 90     n2 = Pop(stack);
 91     n1 = Pop(stack);
 92     
 93     switch(oper){
 94         case '+':
 95             res = n1 + n2;
 96             break;
 97         case '-' :
 98             res = n1 - n2;
 99             break;
100         case '*' :
101             res = n1 * n2;
102             break;
103         case '/' :
104             res = n1 / n2;
105             break;
106         default:
107             res = 0;
108     }
109     return res;
110     
111 }
112 
113 
114 
115 /* SimpleCal.c ends here */

      注: 

  1) 写此代码时,我借用了同作者的另一本经典之作《C语言的科学与艺术》中的几个有用的库,如genlib,strlib等,有了这几个库,屏蔽了一些底层的操作,如malloc使用及错误检测,字符串的相关操作更方便。这里我把这部分代码打成一个静态库extend,再写代码直接引用即可,即提高开发效率,又增强程序健壮性,减少重复检测错误等必不可少而又繁琐的代码,使简单封闭后的C语言而简单实用了。相关代码我附在后面附件中了。

  2)  Makefile文件是我从网上搜索的一个通用的模板,简单设置一下产物,及参数即可使用,详情参考原博客:http://blog.csdn.net/cc_husyand/article/details/6135356


小结:
此例子虽简单,但具备了模块化,层次化开发的特征,好好体会ADT在编程中的威力

 后续以火车厢重排问题来继续体会ADT的优势。

附件:

简单后缀式四则运算计算器代码:https://files.cnblogs.com/linux-sir/simple_cal.zip
《C语言的科学与艺术》扩展库代码:https://files.cnblogs.com/linux-sir/commonlib.zip