程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C語言實現通用數據類型棧

C語言實現通用數據類型棧

編輯:關於C語言

 

C++的模板機制,給通用數據類型編程帶來了很大的方便.那麼C語言能否實現與此類似的 功能呢?當然可以,看一看C的某些庫函數接口就知道了,比如快速排序函數便是通用數據 類型編程的一個例子:

 

void qsort(void *base, sizet_t nmemb, sizet_t size,

           int(*compar)(const void *, const void *));該函數可以排序任意類型的數據,只要提供正確 的比較函數即可.

那麼,C語言中如何實現通用數據編程?本文將對這個問題作詳細的解析,並最終利用所述 方法,實現一個通用數據類型棧.

 

數據的共性

所謂通用數據類型編程,即對於不同類型的數據,完成相同的操作時所使用的代碼也相 同.比如,對於變量賦值操作,不管實際的變量類型是什麼,若均可以通過一條相同的語 句完成賦值操作,則此時的編程是通用數據類型的.此時的代碼可能為:void assign(T var2, T var1);其中assign函數完成賦值操作,var1的值賦給var2,暫且用T表示變量var1和var2的類型.

觀察assign函數,我們不禁要問:若想將var1的值賦給var2,直接使用語句:var2=var1不 就行了嗎,有必要寫一個專門的函數嗎?要回答這個問題,首先需要知道以下兩點:

 

"="本質上是一個函數,與var2=var1等價的函數形式為:=(var2,var1),即名為"=",且 接受兩個參數的函數.

"="只能用於常規類型數據間的賦值操作,而不能用於字符串,結構體等類型的賦值 操作.

因此,為了實現通用數據類型的賦值操作,將該操作寫為一個函數是很有必要的.那麼如 何實現該函數呢?首先分析一下已有數據類型的賦值操作是如何完成的:

 

常規數據類型.對於這類數據,使用"="便可完成變量間的賦值操作,但在賦值的過程 中,真正發生了哪些事情呢?實際上,雖然不同數據類型的賦值語句均相同(均為var2=var1),但其背後發生的事情並不相同.例如若var1,var2均為int型,則語句var2=var1的實際效果是:將var1所占用4個字節內存空間中的內容拷貝到var2所占 用4個字節的內存空間中;但若var1,var2均為char型,則語句var2=var1的實際效果 是:將var1所占用1個字節的內存空間中內容拷貝到var2所占用1個字節的內存空間 中.

由以上分析可知,數據賦值是通過 內存單元的復制實現的,數據類型不同,復制的內 存單元數也不同.

 

字符串.字符串的賦值操作通過 函數strcpy()完成.那麼strcpy()是如何完成字符 串的賦值操作呢?strcpy()的一個簡化實現為:

 

char *strcpy(char *dest, const char *src)

{

    char *desto=dest;

    while(*dest++ = *src++);

    return desto;

} 從代碼可以看出,字符串的賦值實際上為將源字符串所占用內存空間內容拷貝到目 標字符串所占用內存空間.

 

通過以上兩點分析,可以發現一個共性:對於賦值操作,不管數據類型是什麼,均通過拷 貝內存空間內容實現,只不過對於不同的數據類型,拷貝的內存單元數目不同而已.因此,我 們可以根據相同的思路實現我們的賦值函數.那麼此時擺在面前的問題是:如何獲得變 量所占用內存空間,這又相當於以下兩個問題:

 

如何獲取變量所占用內存空間的起始地址;

如何獲取變量所占用內存空間的單元數.

在接下來的內容中,我們將對這兩個問題逐一解答.

 

void指針

獲取變量的起始地址非常容易,因為C語言提供了專門的操作符:"&",取地址操作符.我 們可以將該地址保存在一個指針中,以方便使用.但問題在於:由於我們要實現通用數據 類型編程,而實際的操作數的類型都是某一特定數據類型,那麼我們應該將這個操作數 的地址保存在什麼類型的指針變量中呢?讓我們先設想一下,這個指針不應該是任何特 定類型的指針,但它卻必須幾乎總是被賦值為另一類型的指針,因此其應該能與其它類 型指針方便的進行類型轉換.實際上,C語言提供了這樣的指針:即void指針,該指針的特 性如下:

void指針不與任何數據類型關聯,它可以不使用強制類型轉換,自動的與其它類型指 針進行轉換.

對於void指針不能使用"*"操作符,因為void指針不知道其所指數據所占用的內存單 元數.

void指針的++操作為其地址值增1.

由於不能取消引用一個void指針(第2條),因此它只能用於與其它類型指針轉換.實際上 總是利用這一點來完成通用數據類型編程.這一過程可表示為:

 

 

專用代碼->特定類型指針->void指針->通用代碼->void指針->選定類型指針->專用代碼.

 

從中可看出,void指針是通用代碼與專用代碼間的紐帶,對於需要通用處理的代碼,其與 其它代碼的信息傳遞必須通過void指針,因此其接口必須設計為void指針類型.

 

賦值函數的實現

讓我們回到之前的通用數據類型賦值函數,現在我們完全可以實現它了!實現的思路為: 將它的兩個操作數的指針傳遞給該函數,並指定數據所占用內存空間的單元數,然後在 函數內部將源操作數內存空間內容拷貝至目標操作數內存空間,即可完成賦值操作.那 麼,函數的原型應該為:void assign(void *pvdes, void *pvsrc, sizet size);其中參數的意義為:

 

pvdes:目標操作數指針

pvsrc:源操作數指針

size:數據字節寬度

函數的一個使用實例為:

 

int ia=2;

int ib;

assign(&ib,&ia,sizeof(int)) double da=2.0;

double db;

assign(&db,&da,sizeof(double));

 

 

該例子使用assign函數完成了int型變量和double型變量的賦值操作,雖然變量類型不 同,但assign的調用方式完全相同,因此該函數具有通用數據類型的性質.

 

以上賦值操作的本質是內存的復制,對於這一操作,C語言提供了專門的函數:

 

 

void *memcpy(void *dest, const void *src, sizet n);可以看出,assign()與memcpy()的接口基本相同,因此,在接下來的內容中,我們將直接 使用memcpy()完成賦值操作.

 

通用數據類型棧

在以上內容的基礎之上,離真正的實現一個通用數據類型棧已經不遠了.因為其中最關鍵 的部分是實現變量的賦值,而該問題已經被我們解決.

棧的實現思路為:在棧的初始化過程中,指定棧中元素的數據寬度,將該值保存在棧中一 個變量中;在使用push向棧中保存元素時,采用以上的賦值函數進行數據傳遞;pop操作 與push實現機理相同;棧使用單鏈表保存數據.

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved