第二章為程序設(shè)計技術(shù),本文為2.2.3 內(nèi)置函數(shù)指針和2.2.4 嵌套結(jié)構(gòu)體。
我們知道,數(shù)組和指針是相同類型有序數(shù)據(jù)的集合,但很多時候需要將不同類型的數(shù)據(jù)捆綁在一起作為一個整體來對待,使程序設(shè)計更方便。在C語言中,這樣的一組數(shù)據(jù)被稱為結(jié)構(gòu)體。
>>>2.2.3內(nèi)置函數(shù)指針
面對一系列數(shù)據(jù),真正重要的不是如何存儲數(shù)據(jù),而是如何使用數(shù)據(jù)。實際上,一個結(jié)構(gòu)體的成員可以是數(shù)據(jù),還可以是包含操作數(shù)據(jù)的函數(shù)指針。為了支持這種風(fēng)格,在這里不妨引入一個新的概念——方法是作為某個結(jié)構(gòu)體的一部分聲明的,有了方法就可以操作存儲在結(jié)構(gòu)體中的數(shù)據(jù)。
1.類型與變量
當(dāng)函數(shù)指針作為結(jié)構(gòu)體的成員時,即將校驗參數(shù)和調(diào)用校驗器的函數(shù)指針封裝在一起,形成了一個新的結(jié)構(gòu)體類型。有了類型就可以定義一個該類型的變量,然后就可以用這個變量引用校驗參數(shù)和調(diào)用校驗器函數(shù)。
為了支持這種風(fēng)格,C允許將方法作為某個結(jié)構(gòu)體的一部分來聲明,那么操作存儲在結(jié)構(gòu)體中的數(shù)據(jù)就很容易了,詳見程序清單2.18。
程序清單 2.18 范圍值校驗器接口
接下來需要設(shè)計一個判斷value值是否符合范圍值要求的validateRange()接口函數(shù),其具體的實現(xiàn)詳見程序清單2.19。
程序清單 2.19 范圍值校驗器接口函數(shù)的實現(xiàn)
同理,偶校驗器OddEvenValidator和變量oddEvenValidator的定義詳見程序清單2.20。
程序清單 2.20 偶校驗器接口
接下來同樣需要設(shè)計一個判斷value值是否符合偶校驗要求的validateOddEven()接口函數(shù),其具體的實現(xiàn)詳見程序清單2.21。
程序清單 2.21 偶校驗器接口函數(shù)的實現(xiàn)
顯然,無論是什么校驗器,其共性是value值合法性判斷,因此可以共用一個函數(shù)指針,即特殊的函數(shù)指針類型RangeValidate和OddEvenValidate被泛化成了一般的函數(shù)指針類型Validate。其次,由于每個函數(shù)都有一個指向當(dāng)前對象的pThis指針,因此特殊的結(jié)構(gòu)體類型struct _RangeValidator*和struct _OddEvenValidator *被泛化成了void *類型,即可接受任何類型數(shù)據(jù)的實參。比如:
這就是范型編程,校驗器泛化接口的實現(xiàn)詳見程序清單 2.22。由于pRangeValidator與pThis的類型不同,因此必須對pThis指針強制類型轉(zhuǎn)換才能引用相應(yīng)結(jié)構(gòu)體的成員。
程序清單 2.22 通用校驗器接口的實現(xiàn)(validator.c)
由此可見,當(dāng)將方法作為結(jié)構(gòu)體的一部分聲明時,就直接將方法和數(shù)據(jù)打包成為了一個新的數(shù)據(jù)類型RangeValidator。有了RangeValidator類型,就可以創(chuàng)建一個該類型的變量rangeValidator,即可通過rangeValidator引用該結(jié)構(gòu)體的數(shù)據(jù),并調(diào)用相應(yīng)的處理函數(shù)。真正想強化的是由方法定義結(jié)構(gòu)體的思想,而不是實現(xiàn)結(jié)構(gòu)體時碰巧用到的那些數(shù)據(jù)。
2.初始化
使用名為newRangeValidator的宏將結(jié)構(gòu)體初始化:
其中,validateRange為范圍值校驗器的函數(shù)名,使用方法如下:
宏展開后如下:
其相當(dāng)于:
如果有以下定義:
即可通過pValidator引用RangeValidator的min和max。校驗函數(shù)的調(diào)用方式如下:
以上調(diào)用形式的前提是已知pValidator指向了確定的結(jié)構(gòu)體類型,如果pValidator將指向未知的校驗器,顯然以上調(diào)用形式無法做到通用,那么將如何調(diào)用?
雖然pValidator與&rangeValidator.validate的類型不一樣,但它們的值相等,因此可以利用這一特性獲取validateRange()函數(shù)的地址。比如:
其調(diào)用形式如下:
3.接口與實現(xiàn)
為了便于閱讀,如程序清單2.23所示詳細(xì)地展示了通用校驗器的接口。
程序清單 2.23 通用校驗器接口(validator.h)
以范圍值校驗器為例,調(diào)用validateRange()的rangeCheck()函數(shù)的實現(xiàn)如下:
rangeCheck()函數(shù)的調(diào)用形式如下:
由此可見,rangeCheck()函數(shù)的實現(xiàn)不依賴任何具體校驗器。 注意,在這里,作者并沒有提供完整的代碼,請讀者補充完善。
>>>2.2.4嵌套結(jié)構(gòu)體
1.重構(gòu)
隨著添加一個又一個功能,處理一個又一個錯誤,代碼的結(jié)構(gòu)會逐漸退化。如果對此置之不理,這種退化最終會導(dǎo)致糾結(jié)不清,難以維護(hù)的混亂代碼,因此需要經(jīng)常性地重構(gòu)代碼扭轉(zhuǎn)這種退化。
重構(gòu)就是在不改變代碼行為的前提下,對其進(jìn)行一系列小的改進(jìn),旨在改進(jìn)系統(tǒng)結(jié)構(gòu)的實踐活動。雖然每個改進(jìn)都是微不足道的,甚至幾乎不值得去做,但如果將所有的改造疊加在一起時,對系統(tǒng)設(shè)計和架構(gòu)的改進(jìn)效果是十分明顯的。
在每次細(xì)微改進(jìn)后,通過運行單元測試以確保改進(jìn)沒有造成任何破壞,然后才去做下一次改進(jìn)。如此往復(fù)周而復(fù)始,每次改進(jìn)后都要運行,通過這種方式保證在改進(jìn)系統(tǒng)設(shè)計的同時系統(tǒng)能夠正常工作。
重構(gòu)是持續(xù)進(jìn)行的,而不是在項目結(jié)束時、發(fā)布版本時、迭代結(jié)束時、甚至每天下班時才進(jìn)行。重構(gòu)是每隔一個小時或半個小時就要去做的事情,通過重構(gòu)可以持續(xù)地保持盡可能干凈、簡單且有表現(xiàn)力的代碼。
大量的實踐證明,重復(fù)可能是軟件中一切邪惡的根源,許多原則和實踐規(guī)則都是為了控制與消除重復(fù)而創(chuàng)建的。消除重復(fù)最好的方法就是抽象,即將所有公共的函數(shù)指針移到一個單獨的結(jié)構(gòu)體中,創(chuàng)建一個通用的Validator類型校驗器。也就是說,如果兩種事物相似的話,必定存在某種抽象能夠統(tǒng)一它們,因此消除重復(fù)的行為會迫使團隊提煉出許多的抽象,進(jìn)一步減少代碼之間的耦合。
自從發(fā)明子程序以來,軟件開發(fā)領(lǐng)域的所有創(chuàng)新都是在不斷嘗試從源代碼中消滅重復(fù),即DRY(Don't Repeat Yourself)原則——別重復(fù)自己,因為重復(fù)黏貼會帶來很多的問題,所以無論在哪里發(fā)現(xiàn)重復(fù)的代碼,都必須消除它們。
2.類型與變量
實際上,不管是范圍值校驗器還是奇偶校驗器,其本質(zhì)上都是校驗器,其相同的屬性是校驗參數(shù)和待校驗的值,其相同的行為可以共用一個函數(shù)指針調(diào)用不同的校驗器。根據(jù)依賴倒置原則,將它們相同的屬性和行為抽象為一個結(jié)構(gòu)體類型Validator。比如:
在這里,還是以范圍值校驗為例,在RangeValidatro結(jié)構(gòu)體中嵌套一個Validator類型的結(jié)構(gòu)體,即將Validator類型的變量isa作為RangeValidator結(jié)構(gòu)體的成員。比如:
由于&rangeValidator與&rangeValidator.isa的值相等,因此以下關(guān)系恒成立。比如:
即可將validateRange()函數(shù)原型:
中的“void *pThis”轉(zhuǎn)換為“Validator *pThis”,validatrRange()函數(shù)原型進(jìn)化為:
3.初始化
當(dāng)將Validator類型的isa作為RangeValidator結(jié)構(gòu)體成員時,顯然rangeValidator.isa是一個結(jié)構(gòu)體變量名,可以象任何普通結(jié)構(gòu)體變量一樣使用。使用Validator類型表達(dá)式:
即可引用rangeValidator變量的結(jié)構(gòu)體成員isa的成員validate,即將rangeValidator.isa作為另一個點操作符的左操作符。比如:
由于點操作符的結(jié)合性是從左向右的,因此可以省略括號。其等價于:
只要將rangeValidator.isa看作一個Validator類型的變量即可。
使用名為newRangeValidator的宏將結(jié)構(gòu)體初始化:
其中,validateRange為范圍值校驗器函數(shù)名,使用方法如下:
宏展開后如下:
其中,外面的{}為RangeValidator結(jié)構(gòu)體賦值,內(nèi)部的{}為RangeValidator結(jié)構(gòu)體的成員變量isa賦值。即:
如果有以下定義:
即可用pValidator引用RangeValidator的min和max。
由于pValidator與&rangeValidator.isa不僅類型相同且值相等,則以下關(guān)系同樣成立:
因此可以利用這一特性獲取validateRange()函數(shù)的地址,即pValidator->validate指向validateRange()。其調(diào)用形式如下:
4.接口與實現(xiàn)
以范圍值校驗器為例,validatorCheck()函數(shù)的調(diào)用形式如下:
當(dāng)然,也可以采取以下調(diào)用形式:
其效果是一樣的。
為了便于閱讀,如程序清單 2.24所示詳細(xì)地展示了通用校驗器的接口。
程序清單 2.24通用校驗器接口(validator.h)
以范圍值校驗器為例,調(diào)用validateRange()的validatorCheck()函數(shù)的實現(xiàn)如下:
由此可見,validatorCheck()函數(shù)的實現(xiàn)不依賴任何具體校驗器,通用校驗器接口的實現(xiàn)詳見程序清單 2.25。
程序清單 2.25 通用校驗器接口的實現(xiàn)(validator.c)
在這里,作者并沒有提供完整的代碼,請讀者補充完善。
-
指針
+關(guān)注
關(guān)注
1文章
482瀏覽量
70614 -
C語言編程
+關(guān)注
關(guān)注
6文章
90瀏覽量
21152 -
周立功
+關(guān)注
關(guān)注
38文章
130瀏覽量
37769 -
結(jié)構(gòu)體
+關(guān)注
關(guān)注
1文章
130瀏覽量
10873
原文標(biāo)題:周立功:結(jié)構(gòu)體,使程序設(shè)計更方便——內(nèi)置函數(shù)指針和嵌套結(jié)構(gòu)體
文章出處:【微信號:ZLG_zhiyuan,微信公眾號:ZLG致遠(yuǎn)電子】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
周立功教你學(xué)C語言編程與程序設(shè)計:這樣寫函數(shù)指針數(shù)組最好用
![<b class='flag-5'>周</b><b class='flag-5'>立功</b><b class='flag-5'>教你</b><b class='flag-5'>學(xué)</b><b class='flag-5'>C</b><b class='flag-5'>語言</b><b class='flag-5'>編程</b>與<b class='flag-5'>程序設(shè)計</b>:這樣寫<b class='flag-5'>函數(shù)</b><b class='flag-5'>指針</b>數(shù)組最好用](https://file1.elecfans.com//web2/M00/A6/CE/wKgZomUMQJSAA_OSAAGcJgVXLYk241.png)
周立功教你學(xué)程序設(shè)計結(jié)構(gòu)體:內(nèi)存對齊和基本數(shù)據(jù)類型
![<b class='flag-5'>周</b><b class='flag-5'>立功</b><b class='flag-5'>教你</b><b class='flag-5'>學(xué)</b><b class='flag-5'>程序設(shè)計</b><b class='flag-5'>結(jié)構(gòu)</b><b class='flag-5'>體</b>:內(nèi)存對齊和基本數(shù)據(jù)類型](https://file1.elecfans.com//web2/M00/A6/CF/wKgZomUMQJSAS7J-AAFpZfdaQ_M704.png)
高手們幫忙看下這個結(jié)構(gòu)體嵌套程序,編譯無法通過
新書創(chuàng)作談:周立功教授數(shù)十年之心血力作《程序設(shè)計與數(shù)據(jù)結(jié)構(gòu)》
【完整資料】《程序設(shè)計與數(shù)據(jù)結(jié)構(gòu)》周立功數(shù)十年心血力作
漫談C語言結(jié)構(gòu)體
單片機C語言 -- 基于結(jié)構(gòu)體的面向?qū)ο?b class='flag-5'>編程技巧
《程序設(shè)計與數(shù)據(jù)結(jié)構(gòu)》【完整資料】分享!
C語言結(jié)構(gòu)體對函數(shù)指針封裝示例
C語言入門教程-指向結(jié)構(gòu)體的指針
C語言程序設(shè)計介紹
C語言程序設(shè)計教程之結(jié)構(gòu)體與共用體的詳細(xì)資料說明
![<b class='flag-5'>C</b><b class='flag-5'>語言</b><b class='flag-5'>程序設(shè)計</b>教程之<b class='flag-5'>結(jié)構(gòu)</b><b class='flag-5'>體</b>與共用<b class='flag-5'>體</b>的詳細(xì)資料說明](https://file.elecfans.com/web1/M00/86/0B/o4YBAFx5BSWAcFnGAAHbzzQYZAI425.png)
C程序設(shè)計基礎(chǔ)-數(shù)組與結(jié)構(gòu)體
![<b class='flag-5'>C</b><b class='flag-5'>程序設(shè)計</b>基礎(chǔ)-數(shù)組與<b class='flag-5'>結(jié)構(gòu)</b><b class='flag-5'>體</b>](https://file1.elecfans.com/web2/M00/81/BB/wKgZomQABVGAf9kaAABQpH4-f_0167.jpg)
結(jié)構(gòu)體與指針的關(guān)系
![<b class='flag-5'>結(jié)構(gòu)</b><b class='flag-5'>體</b>與<b class='flag-5'>指針</b>的關(guān)系](https://file.elecfans.com/web2/M00/09/16/pYYBAGD0FvWAeWpFAAAb1Fux1oA753.jpg)
評論