C編程思想之抽象數據類型(ADT)(一)


    在讀了《程序設計抽象思想-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的優勢。

附件:

簡單后綴式四則運算計算器代碼:http://files.cnblogs.com/linux-sir/simple_cal.zip
《C語言的科學與藝術》擴展庫代碼:http://files.cnblogs.com/linux-sir/commonlib.zip


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM