Unix(Linux) C程式設計問題精粹(轉)

BSDLite發表於2007-08-17
Unix(Linux) C程式設計問題精粹(轉)[@more@]文章目錄
第一章:前言
第二章:約定
第三章:開始任務
第四章:使用lint
第五章:使用make
第六章:優質無錯程式設計
第七章:除錯技術
第八章:其它更好的文件

第一章:前言


對於c語言,有人認為它已經落伍了.對於這個問題,仁者見仕,智者見智.的確,c++比c有更強大的諸多優勢.但c++是建立在c之上的.這也是herbert schildt所著的<>在全世界暢銷不衰的原因.更何況,要深入學習linux就必需要有相當的c功底.(這也是我搜集整理本文的根由:-)
現結合個人在程式設計中的體會,為使新手少走彎路,為老手錦上添花,因此無論你是使用c或c++程式設計,也無論你是程式設計的初學者還是成熟的專業人員,均會發現,本文將會對你有所收益.當然,我盡力寫得清晰易懂,又不古板.
我愛c.(正如世人愛上帝一樣:-)..

你可以在forum.linuxaid.com.cn上獲得此帖的文字.而其html版本正在趕製之中......
如果你是在一個月之後看到本文,那麼此文或許已經更新了:-)


第二章:約定

專業的源程書寫風格.
先看看世界級c大師的源程書寫風格.如 steve maguire 就有許多不錯的建議.

[]倡導使用易於理解的"匈牙利式"的命名約定.
所有的字元變數均以ch開始; 如: char ch_****;
所有的位元組變數均冠以b; 如: byte b_****;
所有的長字變數均冠以l; 如: long l_****;
所有的指標變數均冠以p; 如: char *p_ch_****;
建議型別派生出的基本名字之後加上一個以大寫字母開頭的"標籤".如:
分析 char **ppchmydata;
其讓人一眼就能看出它代表一個指向字元指標mydata的指標.
"匈牙利式"命名的最大不足是難念:-(( .但相對於不是總統演講稿的c源程來說,這又算得了什麼?想想看以下的資料命名:
char a,b,c;
long d,e,f;
.
.
.
(反正我是不會再看下去了...)

[]倡導規範書寫.
如果你思如泉湧,而不去也不及顧慮書寫格式,那也沒關係.在將其交出去之前,用cb命令格式化你的源程.雖然源程的格式不會影響到你編譯結果的正確性,但切記,能讓其他的程式設計師能輕鬆地閱讀它.否則沒人會理你的.
關於cb命令的更多用法,可以用man cb來參考其手冊頁.
當然除了cb之外,還有更多更好的.但cb是你在任何unix(linux)上都找得到的.更何況它並不差.


第三章:開始任務

開始任務之前,先做個深呼吸!



[]其他文件你準備好了嗎?
你是不是除了c源程之外一無所有了嗎?兵馬未動,糧草先行.你必須先清楚該程式所要完成的功能.在開始寫程式之前,對程式的功能應有規範說明.書寫規範書和確知程式功能的一個方法是先編寫相應的操作手冊.如果你是一人單幹,勸你首先寫需求書.切記切記,這對你意味著事半功倍的大好事.
一個例項:我計劃為本行的信貸子功能模組打一個補丁.我用10周的時間用來寫規劃書,需求書,操作流程,使用說明等等文件.之後用2周的時間編寫程式,在初步測試(1周)後遞交給各信貸部門測試使用.然後根據反饋的資訊再更改相應文件,並根據文件修改源程.6個月後釋出正式版.

[]一定該遵循ansi標準嗎?
如果你僅使用ansi的標準首標檔案,恭喜你,你的程式有著全世界範圍內的廣泛支援和相容.光明無限.但你必須在通用與專用之間做出取捨,對不起,我幫不了你.
我的原則是:核心用ansi,介面按需而取.這樣在轉換平臺時僅需另編使用者介面而已.實用至上嘛.
附:ansi 標準c標頭檔案






是不是很寒酸?

[]再續前緣?
在得到新任務之後並在開始該新任務之前應馬上回想有哪些是曾經擁有的.舊調重彈遠比另起爐灶來的高效與環保.

[]是否該有自已的庫?
我的答案是應該有自已的特色庫,並與ansi相容.與3.8不同的是,你僅需在源程式之後附上自已的專用庫就可以了.其次在有了自已的庫後,原始碼會很精煉的.不用去羨慕別人了吧.

[]要學會條件編譯.注意你的平臺特性.(高手的標誌?)
除非你確定你要寫的程式是在某特定的os特定的硬體平臺而量身定做.否則應注意資料型別的長度,精度都是不同的,不要想當然.有時甚至是不同的編譯器的差異都要考慮考慮.

....
....(歡迎您來充實此處空白)
....

好了,在任務中,又有哪些細節呢?

[]我是不是葛郎臺?
不要那麼吝嗇.在源程式中加入詳盡的註釋以使自己和他人即使在許多年以後仍能讀明白它是什麼樣的程式.
用註釋行分離各個函式.

[]刪除不需要的程式碼時要小心.
一個好建議是:使用#ifdef del,而不是簡單地註釋掉甚至是粗暴地直接dd.如果你是使用/* ... */,但一旦要刪除的程式碼有很多行,或註釋中以有註釋時,這就可能不那麼好使了.

[]如何給源程式檔案命名?
表現特色且不與任何原有應用名相同.一個簡單地方法就是試試看,系統有什麼樣地反應?

[]一次只修改一個地方.

[]一次只編寫一個單一功能的函式。

[]編寫通用程式.
只有當程式編寫完,並且完成了所需要的效能要求之後,再反過頭來最佳化該程式.

[]不要使用a.out作為結果.你大可以使用與源程相同的可執行檔名.

[]是否一定要用vi編輯?
linux下有許多專用程式設計編輯器.它們能使你有更高的效率和更低的低階輸入錯誤,但我還是要勸你至少要熟練掌握vi.畢竟vi遍地開花.

[]協同作業.請相信,你不是在孤軍作戰.因此,你有必要熟練掌握一些其它的工具.如


....
....(歡迎您來充實此處空白)
....


第四章:使用lint

lint沒有你想象中的那樣糟糕.相反,一旦源程式形成了沒有lint錯誤的形式,將很容易保持下去,並享受到如此而帶來的好處.

[]在cc(gcc)之前就應使用lint.
lint是一語法檢查程式,對於這個多嘴的婆婆來說,你應有足夠的耐心.雖然你知道自已在幹什麼,但在cc之前使用lint總是一個好習慣.

[]lint有哪些特色?
在編譯之前使用lint的重要原因是lint不但能發現ansi c中的語法錯誤,而且也能指出潛在的問題或是難於移植於另一機器的程式碼問題.除了能指出簡單語法錯誤之外,linut還能基於以下原因指出另外的錯誤:
a.無法達到的語句.
b.沒有進入迴圈.
c.沒有被使用的變數.
d.函式引數從未使用.
e.沒有賦值之前自動使用引數.
f.函式在有些地方有返回值,但在其他地方不返回.
g.函式呼叫在不同地方使得引數個數不同.
h.錯誤使用結構指標.
i.模糊使用運算子優先順序.
呵呵呵,挺有用的吧!

[]如何控制lint的輸出?
有時lint會有一大屏一大屏的警告資訊.但似乎並未指出錯誤.為了找出潛在的錯誤則需費心費力地瀏覽這些大量的警告資訊.
但如果你的程式會分出幾個獨立的模組,在初級啟動lint時不要用可選項.當對這些模組進行更改或擴充時,可以忽略與程式碼無關的某些警告.為此可用以下選擇項:
-h 對判別是否有錯,型別是否正確不給出啟發式測試.
-v 不管函式中沒有定義的引數
-u 不管被使用的變數和函式沒有定義或定義了但沒有使用.

[]乾脆,在程式中插入指令來影響lint執行.它看樣子有些像註釋.
/*notreached*/ 不可達到的程式碼不給資訊說明.
/*varargsn*/ 函式的變數個數不作通常的檢查,只檢查開始n個引數的資料型別.
/*nostruct*/ 對下一個表示式不作嚴格型別檢查.
/*argused*/ 下一函式中,不給出沒被使用引數的警告資訊.
/*lintlibrary*/ 置於檔案的開頭,它將不給出沒被使用函式的警告資訊.

關於lint的更多用法,請用man lint來獲知.


第五章:使用make

[]什麼是make?

unix(linux)是一個天生的開發平臺,我為此感到高興.make是一個強力的工具.它能自動跟蹤相互依賴的原始碼塊並組成一程式,使得很容易建立一可執行程式.make就是這種有依賴關係的部分和程式碼之間所作的規格說明.


[] 所有的程式都要使用make?
是的.儘管你只有幾個簡單的模組,但你需要有一種結構來支援它從簡單走向複雜.除非你的程式已經蓋棺定論.

[]makefile由哪些組成?
makefile由以下幾個部分組成:

註釋.
^^^^
使用#符號插入.make將忽略#之後的任何內容以及其後的return鍵.

變數.
^^^^
make允許定義與shell變數類似的有名變數.比如,你定義了sources=prog.c,那麼該變數的值$(scoures)就包含了原始檔名.

依賴關係.
^^^^^^^^
左邊是目標模組,後接一冒號.再接與該模組有依賴關係的模組.

命令.
^^^^
以tab鍵開始(即使用相同數量的空格也不能代替它).


[]makefile示例
下面介紹一個簡單的示例來說明make的用法.假設你的程式有兩個原始檔main.c和myc.c,一個位於子目錄include下的標頭檔案myhead.h,一個庫由三個原始檔myrout1.c,myrout2.c,myrout3.c產生.
其makefile檔案為:
#一個基本的makefile檔案.
#其中包括個人的標頭檔案和個人庫.
headers=include/myhead.h
sources=main.c myc.c
product=$(home)/bin/tool
lib=myrout.a
libsoures=myrout1.c myrout2.c myrout3.c
cc=cc
cflags=-g
all:$(product)
$(product):$(sources)
$(cc)$(cflags) -o $(product)$(sources)
lint:$(product)
lint $(sources)$(libsources)
哈哈,挺象shell程式設計的.如果你與我一樣使用linux下的gcc,那麼只要把上面的cc=cc改為cc=gcc即可.怎麼樣,想來一個更復雜點的嗎?

[]一個更為複雜的makefile
你是否注意到,在上例中,只要啟動make,就會重新編譯所有原始碼.
如果你能看懂以下的makefile,恭喜恭喜,你通關了.
#一個更為複雜的makefile
headers=include/myhead.h
soures=main.c myc.c
objects=main.c myc.c
product=$(home)/bin/tool
lib=myrout.a
libsources=myrout1.c myrout2.c myrout3.c
libobjects=$(lib)(myrout1.o)$(lib)(myrout2.o)$(lib)(myrout3.o)
include=include
cc=cc
cflags=-g -xc
lint=lint
lintflags=-xc
all:$(product)
$(product):$(objects)$(lib)
$(cc)(cflags)-o$(product)$(objects)$(lib)
.c.o: $(headers)
$(cc)$(cflags) -c i$(include)$<
$(lib):$(headers)$(libsources)
$(cc) $(cflags) -c $(?:.o=.c)
ar rv $(lib) $?
rm $?
.c.c:;
lint: $(product)
$(lint)$(liniflags)$(sources)$libsources)




第六章:優質無錯程式設計


親愛的,檢查一下,你是否注意到了以下的細節?也就是說,你是否是一個合格的,能編寫優質無錯程式碼的程式設計師?要永遠記住,編寫無錯程式碼是程式設計師的責任,而不是測試員.(摘錄於本人的"細節頁",因此本節將永遠不會保持完整,歡迎您來充實她)

[]所有程式設計師至少出現過的一個錯誤:
if(a=3){......}如果a等於3,那麼......
你至少要養成這樣的習慣:當判斷一個變數與一個常量是否相等時,將常量寫在前面.這樣即使你一不小心寫成這樣:if(3=a){......}在cc 之前就可以很容易發現它.


[]老調重彈:邏輯運算子的優先權.
我不願多嘴.總之,如果你一定要編寫如下程式碼時:
if(a&0x1&&b&0x2){......}
你的手頭最好有一本詳盡的指南.或者你是這方面的專家.

[]儘量不使用int資料型別.
這僅是一個忠告.你大可使用char,short,long資料型別.若干年以後,當你成長為高手之時,你會發現此時我的良苦用心.

[]對於非整型函式一定要完整定義.
如 long float jisuan(char charr[],int chnum)
{ long float lmydata;
...
...
return(lmydata); }

[]對於非整型函式的輸入要當心.
如 long float lfnum;
...
...
scanf("%lf",&lfnum);

[]float 型的有效數字為7位.當多於7位時,第8位及以後的位將不準確,可以將其定義為long float型.

[]檔案的輸入出盡量採用fread fwrite函式.只有當另有用途時才用fprintf fscanf 函式.

[]對於陣列及字串的比較操作時要確認以'結束.

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617542/viewspace-963852/,如需轉載,請註明出處,否則將追究法律責任。

相關文章