劉 鵬,趙仁慶,鄧燕林
(楚雄師范學院數學系,云南 楚雄 675000)
C語言是一種國際上廣泛流行的計算機高級語言,具有用途廣泛、功能強大、使用靈活的特點。《C語言程序設計》是高等學校的一門基本的計算機課程,在計算機教育和計算機應用中發揮著重要的作用。通過學習程序設計,學生進一步了解計算機的工作原理,更好地理解和應用計算機;掌握用計算機處理問題的方法,能培養分析問題和解決問題的能力,具備編制程序的初步能力[1]。
C語言是大學階段第一門程序設計的課程,有了C語言的基礎,在以后學習高級語言編程就會很容易。在該課程的教學中,如何來培養學生的編程能力呢?
結構化程序設計的思想是自頂向下,逐步細化,模塊化設計,結構化編碼。核心的理念就是層次。良好的代碼要有層次感,先做好“頂層設計”,然后一步一步細化,這樣做可使編碼工作有條不紊地進行。
在教材第291頁有這樣的題目[1]:有n個人圍成一圈,順序排號。從第1個人開始報數 (從1到3報數),凡報到3的人退出圈子,問最后留下的是原來第幾號的那位。文獻[2]在第104頁給出如下的參考解答[2]:

這段代碼從頭到尾只有一個main()函數,根本就不符合結構化程序設計的思想要求。
把所有的代碼都堆在main()函數中,就如同在一個硬盤中存放了很多文件,卻沒有使用任何文件夾來進行管理一樣;也如同買了一套房子,并沒有用墻把房子分隔成臥室、客廳、廚房、餐廳、衛生間,而是將一切生活用品都堆放在房子的地上,吃喝拉撒都在一個大房間里進行。
一個源程序由若干個函數組成,猶如用墻把房子分隔成一個個房間一樣。模塊化設計就是把一個大任務分為若干個相對簡單的子任務,每個子任務用函數來實現。
解決一個問題首先要從大處著手,做好“頂層設計”。站在高處來看,解決這個問題需要四個步驟[3]:輸入人數;數組初始化;“從1到3報數”n-1次;輸出最后剩下的那個人的編號。而每一步都可以簡單地用函數或基本的控制語句來實現。在向下一層展開之前應檢查本層設計是否正確,只有上一層設計是正確的才能向下細化。良好的程序設計應該是把復雜的問題分解為簡單的問題,然后各個擊破,一件一件有條不紊地完成。
“自頂向下,逐步求精”的層次理念是每個編程人員時刻都要堅持的,就像做人要守好自己的“底線”一樣,不能輕易發生動搖。
在教學的過程中,可以總結出一個編程的標準模板。利用這個模板,可幫助學生快速搭建程序框架,讓學生能集中精力于數據的處理、問題的解決方面。這個模板為:

下面就以求兩個正整數的最大值為例進行說明:
第一:預處理命令#include<stdio.h>必不可少,另外可能用到的有#include<math.h>、#include<string.h>。若程序中有一些常數,建議用預處理命令將其定義為符號常量:#define N 10,另外要注意預處理命令后面沒有標點符號。
第二:函數聲明由函數定義中的首行,再加一個分號“;”形成,此時形參名可省略不寫。形參名是什么都無所謂,特別注意函數聲明是以分號“;”結束的。
第三:根據問題的需要,定義有關的變量,在使用的過程中一定要注意與定義時的變量類型要保持一致。全局變量一般是在不同的地方進行傳遞數值的。
第四:數據的輸入主要有以下幾種方式:①賦值:在定義時進行賦值即為初始化,如數組的初始化;②鍵盤輸入:特別注意做好數據輸入 (出)時的提示設計,如:

③加入數據的合法性檢查:數據的合法性檢查是數據輸入時要做的事,當用戶輸入不合法的數據,程序提示用戶重新輸入,如:

第五:數據處理是程序設計編寫的核心,一般使用函數來完成,這樣一來使設計者的精力放在后面的函數定義上即可:c=max(a,b);。
第六:數據的輸出主要用函數printf()實現:

字符數據的輸入輸出函數有:putchar,getchar,puts,gets。
第七:函數定義就是程序設計者要花時間精力去做的事情,如:


有了模板后,要寫好程序就需要寫好函數,而要寫好函數就必須熟悉一些基本的算法和常見問題的求解方法。
第一個問題:用輾轉相除法,求解整數x和y的最大公約數的算法如下[4]:
步驟1:變量r是x除以y的余數;
步驟2:變量r不是0的時候,反復執行步驟3~5;
步驟3:變量x代入變量y的值;
步驟4:變量y代入變量r的值;
步驟5:變量r是x除以y的余數;
步驟6:變量y的值即為最大公約數;
使用基本語句:

一般來說,只要能得到問題的解法,就很容易把它寫成函數的形式。該問題從使用窮舉法、基本語句到使用函數、遞歸函數的過程,體現了編程中精益求精的思想,極能體現編程思維的抽象性和深刻性。
第二個問題:判斷鍵盤輸入的整數 (用n來表示)是否是素數:

根據數學上的結論,循環只需到n的算術平方根就可以了。但此時不能簡單地用sqrt(n)來求n的平方根或整數部分,由1=12,1+3=22,1+3+5=32,…,1+3+…+(2k-1)=k2,可求出n的整數部分。整個程序如下[3]:


素數問題本身就是初等數論中的重要問題,解決該問題的程序效率與所掌握的有關數學理論知識密切相關,這里給出的求n的整數部分的方法值得程序設計者深思。
第三個問題:級數求和中典型的例子是[1]:用π/4≈1-1/3+1/5-1/7+…公式求π的近似值,直到某一項的絕對值小于106為止 (該項不累加)[1]。

該問題之所以重要是因為涉及到循環累加求和基本方法、交錯數列符號處理技巧,真正掌握該編程思想就能處理許多求和的問題。
這類問題還很多,需要在學習過程中不斷地歸納總結。通過對這些問題的解決,可訓練學生的編程思維,進一步提高學生的編程能力。
現在介紹IT行業優秀工程師的經驗方面的書籍很多,里面有許多值得在教學中推廣的做法。當然,在以往教學中也存在許多錯誤的觀念,會嚴重影響教學的質量。
一些正確的觀念,在教學之初就要強調:①浮點數是實數在計算機上的近似表示,只能表示一部分實數;②程序設計中,遇到常數一般定義為符號常數,避免魔法數問題 (程序中的常數與問題沒有直接關系);③寫循環時一定要檢查邊界條件,避免造成失之毫厘,差之千里的錯誤;④指針是某個對象存儲時占用若干連續存儲單元中首個存儲單元的地址。這方面的問題建議大家好好讀讀參考文獻[3]、[4]~[8]。
許多初學者都會有這樣的想法:只要編寫出程序,運行結果也沒有問題就萬事大吉了。實際上這是一個很嚴重的錯誤觀念!比如求100~200間的全部素數的程序如下[1]:


該程序編譯、連接都沒問題,執行結果如下

從數學的角度上來說結果也沒有錯誤。但該程序隱藏著一個很難發現的錯誤[3]:語句if(m%10==0)printf(" ");的正確位置是在m=m+1;之后 (應與前面的花括號互換位置)!程序中if(m%10==0)printf(" ");語句的意思是前面輸出了10,20,30,……個素數時就換行,即只要前面輸出的素數個數是10的倍數就換行。該問題中第10個素數與第11個素數以及第20個素數與第21個素數恰好相鄰,掩蓋了這個錯誤。將語句中的10改為其他正整數如3、4等,錯誤馬上就凸顯出來。
實際上這里使用該語句是想每行輸出10個素數,只有語句if(m%10==0)printf(" ");緊隨語句m=m+1;之后,才能真正實現這一目的。
教師平時都會告誡學生:看懂學會書上的理論知識,學習只成功了30%;動手編程,親自上機調試運行,可達60%;善于利用所得到的知識解決實際問題,才能達到90%以上。學習C語言程序設計,必須重視實踐環節,既會編寫程序,又會上機實踐,另外還要舉一反三。
《C語言程序設計》課程只是程序設計學習過程中的起點,在今后的學習過程中,還需要進一步學習其他高級程序語言,遇到許多實際問題時要有編程解決的意識。只有養成用程序設計來解決實際問題的良好習慣,才能使程序設計的學習得到深化,程序設計能力才可能會得到提高。
如學生在后續課程《信息論與編碼》中需要計算信息熵:

其中p1,p2,…,pn為概率分布:p1+p2+…+pn=1,pi〉0。
C語言數學庫中沒有log2(x)函數,可用換底公式來計算:log2(x)=log(x)/log(2)。函數的參數個數是變化的,C語言中沒有專門的函數,參考程序為:

《C語言程序設計》課程教學中學生編程能力的培養問題是一個系統工程,只有堅持不懈地努力,才能收到良好的效果。
[1]譚浩強.C程序設計 (第四版)[M].北京:清華大學出版社,2010:9,131-132,137-138,291.
[2]譚浩強.C程序設計 (第四版)學習輔導[M].北京:清華大學出版社,2010:104.
[3]薛非.拋棄C程序設計中的謬誤與惡習[M].北京:清華大學出版社,2012:86-87,255.
[4]李克秋譯.算法解讀[M].北京:科學出版社,2012:100.
[5]尹哲,鄭秀雯譯.編寫可讀代碼的藝術[M].北京:機械工業出版社,2012.
[6]陳正沖.C語言深度解剖——解開程序員面試筆試的秘密[M].北京:航空航天大學出版社,2010.
[7]崔康譯.程序員的思維修煉開發認知潛能的九堂課[M].北京:人民郵電出版社,2011.
[8]何海濤.劍指Offer名企面試官精講典型編程題[M].北京:電子工業出版社,2012.