昨天我們完成了一個數(shù)組版的List,但是因為數(shù)組的限制,導(dǎo)致那個實現(xiàn)不能收放自如,為了解決這個問題,我們引入指針版本的List,我們還是先來看看接口: typedef struct TelPhone{ /* struct list{ typedef list* List; void InitializeList (List * plist); #endif typedef list* List; -------------------------------------- 大家可能會覺得奇怪,為什么會是這樣的呢?如果這樣取個外號,那么我們下面的List*想要表達(dá)的豈不是list **了嗎?是啊,我們要的就是這種效果,指針的指針,指針的指針很多時候又可以用來表示二維數(shù)組: ------------------------------------- int a[n][m];//二維數(shù)組 int **a;//指針的指針 --------------------------------------- 上面這兩種聲明方式在特定的情況下是可以相互表示。如果說大家能夠很好的理解這個,那么對于我們的List*是不是也很好的理解呢?List*plist等價于list** plist,可以這么來理解,plist是一個指向list指針的指針,既是*plist的指針,而*plist是指向list的指針,就是我們需要的指針,這樣聲明的好處是我們很容讓*plist成為一個空殼,也就是好初始化。說到這里,有人會問,既然是一個指向指針的指針,我們可不可以這么來聲明呢: ---------------------------------- typedef struct TelPhone{ typedef struct node{ struct list{ ----------------------------------------- 這樣我們同樣可以用下面的方式去初始化: ------------------------------------------- *(plist->head) = NULL; ------------------------------------------- 這看上去似乎是沒錯的,而且還可以瞞天過海,當(dāng)然瞞的是編譯器,所以如果這樣寫,編譯不報錯,但是運行時報錯了,這是為什么呢?這個問題大概只有大家學(xué)完C++的動態(tài)類型識別的時候才會得到答案,不過這可能會是很久以后的事?,F(xiàn)在我們回到正題上來,既然我們不能這樣寫,那么我們還是規(guī)規(guī)矩矩的這樣初始化: ---------------------------------- *plist = NULL; ---------------------------------- 那么*plist表示什么呢?我們只知道他是一個指向list的指針,里面包含了我們需要iTem和next,但是我們可能有些朋友并不知道他實際上就我們要實現(xiàn)的list的首地址,所以這就是指針的方便之處,我們得到了一塊內(nèi)存的指針便得到了這塊內(nèi)存的起始處,這個地址對我們來說極為重要,當(dāng)然不只是我們,是所有想要使用指針的程序員來說都很重要。 該說的理論大概都說了,如果還有不懂還有懵懂的,可以看下我們的接口的具體實現(xiàn),如果發(fā)現(xiàn)確實還不懂的話可以直接提問,我想今天這是我們C語言的最后一講了,接下來我們該說說C++或是win32編程的知識了。 -------------------------------------- //my_list.c //全局函數(shù),把元素添加進(jìn)列表 //初始化 //顯示列表中的元素 //釋放內(nèi)存 void FreetheList (List *plist) 接口的實現(xiàn)和我們有問題的實現(xiàn)看上去差不多,大家可以嘗試回頭去看看第二十八講里面的實現(xiàn),再來對比一下我們今天的實現(xiàn),看看問題出在那里,我們在二十八講里面是怎么瞞過大家雙眼的,如果大家能夠把這個地雷挖出來,C語言也算差不多了,至少在指針一塊有所理解了。 int main() 現(xiàn)在我們來思考這次實踐的問題,在這次實踐中,我們雖然反轉(zhuǎn)糾結(jié)多次,但是我們卻沒有修改過驅(qū)動函數(shù)(也就是mian),而且接口我們也沒有修改,我們所動到的知識類型的聲明和接口的實現(xiàn),而我們的mian函數(shù)卻不關(guān)心我們的接口是怎么實現(xiàn)的,他只知道調(diào)用就好,至于怎么實現(xiàn)的與他無關(guān),我們可以任意修改,而不影響main的運行,現(xiàn)在是不是發(fā)現(xiàn)這種做法很有用呢?是啊,我們只要實現(xiàn)一類接口,以后當(dāng)我們想要實現(xiàn)這些功能的時候就無需在一步步的寫代碼,我們直接把我們的頭文件添加進(jìn)去就可以拉出就用,而且通常情況下我們這類接口是可以通用的,如果我們不想要電話薄,我們想要其他的,我們只需要把頭文件里面的iTem結(jié)構(gòu)修改成我們想要就好,而不會影響到我們接口,大家可以回頭去瞧瞧,除了顯示元素那個接口會受到影響外其他的毫無問題,當(dāng)然,為了通用型,我們可以重新寫個顯示元素的接口: --------------------------------------- void ShowListiTem (const List * plist, void (* pfun) (iTem item)); ---------------------------------------- 這個接口看起來有些復(fù)雜(對于初學(xué)者來說),不過不難理解,后面的void(*pfun)(iTem item)是一個函數(shù),以iTem類型為參數(shù)的函數(shù),*pfun是函數(shù)指針,也就是代表一個函數(shù)名,就好比,我們有個打印函數(shù)叫; ----------------------------------- void Show(iTem item); ------------------------------------ 于是我們就可以這樣調(diào)用上面的接口: -------------------------------------------------- ShowListiTem (&BookPhone, Show); --------------------------------------------------- 所以,這個接口的意思就是讓一個函數(shù)作用于我們的List,于是這個接口就變得和類型無關(guān),變得通用。對于這個接口,我們可以這樣來實現(xiàn): ------------------------------------------------ void ShowListiTem(const List * plist, void (* pfun)(iTem item)) while (pnode != NULL) pnode = pnode->next; } --------------------------------------------- 于是我們在我們的main函數(shù)后寫個Show函數(shù)來給這個接口使用: -------------------------------------------- void Show(iTem item) { printf('%-10s:%s\n', } ------------------------------------------------- 這個Show函數(shù)可以根據(jù)我們需要的類型而修改,這樣一來,我們這個List就變得通用了。以后再要使用的時候就會很方便。 最后,大家如果有興趣的話可以自己去嘗試一下vector,stack,二叉查找樹等等,這些我們就不打算說了,原理和我們的List差不多。當(dāng)然這些等到以后我們學(xué)C++的時候還會重點說,那時候你會發(fā)現(xiàn)這些算法模型的功能到底有多強(qiáng)大,C語言部分真的結(jié)束了,因為語法都和大家說了,怎么用就看大家的想法了,在編程里面我比較喜歡“想法”二字,尤其是電影一代宗師里面更是把這二字用得出神入化啊。
===================== 回復(fù)D(不區(qū)分大小寫)直接查看目錄
|
|
來自: 昵稱29398856 > 《第三天》