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