我們之前從底層研究了C語言,也學習了C語言的組成部分和使用方法,但是c語言是一門編程語言,它是要用來編寫程序的。要編寫一個好的程序,首先要弄清這個程序要干什麼,也就是需求分析,之後要思考及確定實現這個目標的方法,怎麼樣讓程序簡短、高效、易懂、可移植、方便維護和修改,這涉及到要怎麼實現算法、怎樣放置數據和代碼、怎麼寫函數、怎麼調用函數等等,這就是程序設計。程序設計是一個程序員綜合實力的體現,要想設計出好的程序,要有程序設計思想。
我們來看程序1,程序的功能是:可以接收用戶依次輸入,一個字符串a、一個字符ch、一個字符串b,這些內容分別描述一個整型數據、一個運算符、一個整型數據,然後根據字符ch所描述的運算符如:“+”、“-”,對字符串a和b的描述數據進行運算,將運算結果顯示出來。
程序的執行結果如下:
這個程序的功能是用戶先輸入一個數,按回車後再輸入加號或減號,再輸入一個數,然後輸出結果。這個功能有有幾個重點:(1)輸入方式,用什麼函數輸入(2)輸入的字符串怎麼轉換成一個數輸出(3)怎麼進行輸入檢驗。我們來看看上面的程序是怎麼實現的:
(1)對於字符串使用gets()函數輸出,對於符號使用getch()函數輸出,因為c語言裡沒有字符串類型,所以這裡定義的是兩個字符數組,在這裡定義的數組長度為20,也就是說用戶最多能輸入長度為20個字節的數據。Gets()函數可以無限讀取,不會判斷上限,以回車結束讀取。Getch()函數可以從控制台讀取一個字符,但是不顯示在屏幕上,但是我們希望能把輸入的符號也顯示出來,所以程序在這裡使用了一個printf()函數輸出getch()函數得到的字符,其實這裡也可以使用getche()函數,它是可以自動將輸入的字符顯示在屏幕上的。
(2)程序裡是用atoi()函數處理輸入的字符串數組的,它的功能是把字符串轉換成整型數,它的參數是const char *,如果第一個非空格字符存在,是數字或者正負號則開始做類型轉換,之後檢測到非數字(包括結束符 \0) 字符時停止轉換,返回整型數。否則,返回零。因為數組名可以看成一個指針,所以這裡的類型是對應的。但是如果用戶輸入的包含不是數字的字符,那麼轉換的結果是0,但是這裡沒有對這種錯誤的檢驗和報錯。
(3)程序對第二個輸入的變量進行了判斷,如果輸入的字符不是加號和減號,那麼會輸出錯誤提示“error!”然後返回程序。
整個程序的邏輯就是先定義數組和變量,再輸入,再判斷輸入,再輸出。所以這裡運算符的地位都是平等的,要將程序擴充為識別“*”、“/”,要在判斷和輸出裡加上“*”、“/”,再在輸出語句裡實現相關算法。修改的程序如下:
如果要寫錯誤判斷,可以用循環判斷數組a和b裡的每個字符,如果為字母,則輸出錯誤提示。
再來看第二個程序:它功能與程序1的相同:
程序二是定義了兩個運算函數add、sub,分別實現將兩個數相加和將兩個數相減的功能。又定義了一個函數指針數組存放這兩個函數的地址。這樣可以通過數組來選擇並調用函數。另外我們定義了一個字符指針code,並初始化為“+-”,然後在main函數裡用for語句判斷輸入的符號是加號還是減號,如果是加號,則for語句結束後n的值為0,如果是減號,則for語句結束後n的值為1。但是這裡有個問題:如果我們輸入的符號不是加號或減號,那麼for語句怎麼跳出呢?我發現把for語句裡的第一個code[n]去掉之後,程序就無法進行下面if語句的判斷了,是不是code[n]指的是循環中n的值最大只能達到數組的長度呢?我們寫一個程序來驗證一下:
輸出結果為:
果然,這樣寫可以限制循環的次數在數組的長度以內,那麼這是為什麼呢?因為之前我覺得有問題是p[i]指向的是內存的值,所以如果字符串之後的內存如果存儲了別的數據判斷就會出錯。但是我忽略了字符串後面會加一個轉義字符“\0”,即數字0,所以這時code[i]為0,即為false,所以循環會停止,而!Code[n]為1,則if語句裡判斷能通過,所以會輸出錯誤信息“error!”。
如果輸入是加號或者減號,則不會執行if語句裡的語句。調用函數指針數組func[c](atoi(a),atoi(b));根據n的值可以調用實現相加或相減的函數,然後輸出函數返回的值即運算的結果��
那麼我們根據程序的實現思路,如果要增加操作符,需要寫一個實現該操作的函數,並將它加入到函數指針數組中,這樣我們就可以通過函數指針數組來調用函數實現功能。修改後的程序如下:
執行的結果如下:
對比程序1和程序2,可以發現程序1的語句都是在main函數裡,它使用的方法比較簡單,但是要增加操作符比較麻煩,要修改程序需要再增加輸出語句並修改判斷方法,而程序2是用子函數來實現一個單獨的功能,並使用函數指針來進行函數的調用,使用函數指針數組來管理函數,這樣所有的輸入、輸出、判斷語句都在main函數中,關於算法的語句都在子函數裡,如果我們要增加新的操作符,只需要寫一個新的子函數並在函數指針數組裡將函數名寫入,在字符指針裡加入要添加的操作符,不需要修改main函數。
這樣可以看出兩個函數的設計思想是不同的,程序1比較符合人的思維,即定義、輸入、判斷、輸出,這樣集成度高,思路清晰,易於實現,但是如果對於比較復雜的程序會造成難以修改和維護的情況,程序2采用模塊化的程序設計思想,用到了比較高級的方法,將功能模塊化,而且用數組進行管理更加有序,便於修改和維護,有更強大的擴展性和通用性。
與程序1相比,程序2中的共性實現在main函數裡,即輸入、判斷、輸出的功能,個性實現在子函數,即add、sub函數裡。它的模塊化的設計思想對於大型程序的開發、管理和維護有十分重要的作用,對於程序設計和優化有很強的指導意義。
有人將程序設計總結成了一些原則,我們來看看這些原則是什麼:
(1)“開-閉”原則:我們設計的程序應當在不被修改的情況下進行擴展,即要做到當增加新功能時不修改原來的程序,只增加新的函數或子程序。上面的程序2就符合這個原則。
(2)迪米特法則:又叫做最少知識原則,即一個子程序最好與其他子程序有最少的溝通和了解,同樣的,一個函數最好盡可能少的調用其他函數或者使用全局變量。
(3)單一職責原則:一個函數應該盡可能地完成單一的工作。
還有一些原則是針對高級語言總結的,這裡就不做探討了。
我們現在寫的程序都是一些小程序,調試和修改都比較容易,所以比較少涉及到程序設計思想。當我們開發大型程序時,程序設計思想就比較重要了。所以我們平時一定要注意培養好的程序設計習慣和正確的程序設計思想。好的設計思路可以減少我們犯錯的頻率和調試的難度。