編程修養(yǎng) (作者:陳皓)
————
?
什么是好的程序員?是不是懂得很多技術(shù)細(xì)節(jié)?還是懂底層編程?還是編程速度比較快?
我覺得都不是。對(duì)于一些技術(shù)細(xì)節(jié)來說和底層的技術(shù),只要看幫助,查資料就能找到,對(duì)
于速度快,只要編得多也就熟能生巧了。
?
我認(rèn)為好的程序員應(yīng)該有以下幾方面的素質(zhì):
?
? 1、有專研精神,勤學(xué)善問、舉一反三。
? 2、積極向上的態(tài)度,有創(chuàng)造性思維。
? 3、與人積極交流溝通的能力,有團(tuán)隊(duì)精神。
? 4、謙虛謹(jǐn)慎,戒驕戒燥。
? 5、寫出的代碼質(zhì)量高。包括:代碼的穩(wěn)定、易讀、規(guī)范、易維護(hù)、專業(yè)。
?
這些都是程序員的修養(yǎng),這里我想談?wù)劇熬幊绦摒B(yǎng)”,也就是上述中的第5點(diǎn)。我覺得,如
————
?
什么是好的程序員?是不是懂得很多技術(shù)細(xì)節(jié)?還是懂底層編程?還是編程速度比較快?
我覺得都不是。對(duì)于一些技術(shù)細(xì)節(jié)來說和底層的技術(shù),只要看幫助,查資料就能找到,對(duì)
于速度快,只要編得多也就熟能生巧了。
?
我認(rèn)為好的程序員應(yīng)該有以下幾方面的素質(zhì):
?
? 1、有專研精神,勤學(xué)善問、舉一反三。
? 2、積極向上的態(tài)度,有創(chuàng)造性思維。
? 3、與人積極交流溝通的能力,有團(tuán)隊(duì)精神。
? 4、謙虛謹(jǐn)慎,戒驕戒燥。
? 5、寫出的代碼質(zhì)量高。包括:代碼的穩(wěn)定、易讀、規(guī)范、易維護(hù)、專業(yè)。
?
這些都是程序員的修養(yǎng),這里我想談?wù)劇熬幊绦摒B(yǎng)”,也就是上述中的第5點(diǎn)。我覺得,如
果我要了解一個(gè)作者,我會(huì)看他所寫的小說,如果我要了解一個(gè)畫家,我會(huì)看他所畫的圖
畫,如果我要了解一個(gè)工人,我會(huì)看他所做出來的產(chǎn)品,同樣,如果我要了解一個(gè)程序員
,我想首先我最想看的就是他的程序代碼,程序代碼可以看出一個(gè)程序員的素質(zhì)和修養(yǎng),
程序就像一個(gè)作品,有素質(zhì)有修養(yǎng)的程序員的作品必然是一圖精美的圖畫,一首美妙的歌
曲,一本賞心悅目的小說。
?
我看過許多程序,沒有注釋,沒有縮進(jìn),胡亂命名的變量名,等等,等等,我把這種人統(tǒng)
稱為沒有修養(yǎng)的程序,這種程序員,是在做創(chuàng)造性的工作嗎?不,完全就是在搞破壞,他
們與其說是在編程,還不如說是在對(duì)源程序進(jìn)行“加密”,這種程序員,見一個(gè)就應(yīng)該開
除一個(gè),因?yàn)樗幍某绦蛩鶆?chuàng)造的價(jià)值,遠(yuǎn)遠(yuǎn)小于需要在上面進(jìn)行維護(hù)的價(jià)值。
?
程序員應(yīng)該有程序員的修養(yǎng),那怕再累,再?zèng)]時(shí)間,也要對(duì)自己的程序負(fù)責(zé)。我寧可要那
種動(dòng)作慢,技術(shù)一般,但有良好的寫程序風(fēng)格的程序員,也不要那種技術(shù)強(qiáng)、動(dòng)作快的“
搞破壞”的程序員。有句話叫“字如其人”,我想從程序上也能看出一個(gè)程序員的優(yōu)劣。
因?yàn)椋绦蚴浅绦騿T的作品,作品的好壞直截關(guān)系到程序員的聲譽(yù)和素質(zhì)。而“修養(yǎng)”好
的程序員一定能做出好的程序和軟件。
?
有個(gè)成語(yǔ)叫“獨(dú)具匠心”,意思是做什么都要做得很專業(yè),很用心,如果你要做一個(gè)“匠
”,也就是造詣高深的人,那么,從一件很簡(jiǎn)單的作品上就能看出你有沒有“匠”的特性
,我覺得做一個(gè)程序員不難,但要做一個(gè)“程序匠”就不簡(jiǎn)單了。編程序很簡(jiǎn)單,但編出
有質(zhì)量的程序就難了。
?
?
我在這里不討論過深的技術(shù),我只想在一些容易讓人忽略的東西上說一說,雖然這些東西
可能很細(xì)微,但如果你不注意這些細(xì)微之處的話,那么他將會(huì)極大的影響你的整個(gè)軟件質(zhì)
量,以及整個(gè)軟件程的實(shí)施,所謂“千里之堤,毀于蟻穴”。
?
“細(xì)微之處見真功”,真正能體現(xiàn)一個(gè)程序的功底恰恰在這些細(xì)微之處。
?
這就是程序員的——編程修養(yǎng)。我總結(jié)了在用C/C++語(yǔ)言(主要是C語(yǔ)言)進(jìn)行程序?qū)懽魃?
的三十二個(gè)“修養(yǎng)”,通過這些,你可以寫出質(zhì)量高的程序,同時(shí)也會(huì)讓看你程序的人漬
漬稱道,那些看過你程序的人一定會(huì)說:“這個(gè)人的編程修養(yǎng)不錯(cuò)”。
?
??? ————————————————————————
?
??????? 01、版權(quán)和版本
??????? 02、縮進(jìn)、空格、換行、空行、對(duì)齊
??????? 03、程序注釋
??????? 04、函數(shù)的[in][out]參數(shù)
??????? 05、對(duì)系統(tǒng)調(diào)用的返回進(jìn)行判斷
??????? 06、if 語(yǔ)句對(duì)出錯(cuò)的處理
??????? 07、頭文件中的#ifndef
??????? 08、在堆上分配內(nèi)存
??????? 09、變量的初始化
??????? 10、h和c文件的使用
畫,如果我要了解一個(gè)工人,我會(huì)看他所做出來的產(chǎn)品,同樣,如果我要了解一個(gè)程序員
,我想首先我最想看的就是他的程序代碼,程序代碼可以看出一個(gè)程序員的素質(zhì)和修養(yǎng),
程序就像一個(gè)作品,有素質(zhì)有修養(yǎng)的程序員的作品必然是一圖精美的圖畫,一首美妙的歌
曲,一本賞心悅目的小說。
?
我看過許多程序,沒有注釋,沒有縮進(jìn),胡亂命名的變量名,等等,等等,我把這種人統(tǒng)
稱為沒有修養(yǎng)的程序,這種程序員,是在做創(chuàng)造性的工作嗎?不,完全就是在搞破壞,他
們與其說是在編程,還不如說是在對(duì)源程序進(jìn)行“加密”,這種程序員,見一個(gè)就應(yīng)該開
除一個(gè),因?yàn)樗幍某绦蛩鶆?chuàng)造的價(jià)值,遠(yuǎn)遠(yuǎn)小于需要在上面進(jìn)行維護(hù)的價(jià)值。
?
程序員應(yīng)該有程序員的修養(yǎng),那怕再累,再?zèng)]時(shí)間,也要對(duì)自己的程序負(fù)責(zé)。我寧可要那
種動(dòng)作慢,技術(shù)一般,但有良好的寫程序風(fēng)格的程序員,也不要那種技術(shù)強(qiáng)、動(dòng)作快的“
搞破壞”的程序員。有句話叫“字如其人”,我想從程序上也能看出一個(gè)程序員的優(yōu)劣。
因?yàn)椋绦蚴浅绦騿T的作品,作品的好壞直截關(guān)系到程序員的聲譽(yù)和素質(zhì)。而“修養(yǎng)”好
的程序員一定能做出好的程序和軟件。
?
有個(gè)成語(yǔ)叫“獨(dú)具匠心”,意思是做什么都要做得很專業(yè),很用心,如果你要做一個(gè)“匠
”,也就是造詣高深的人,那么,從一件很簡(jiǎn)單的作品上就能看出你有沒有“匠”的特性
,我覺得做一個(gè)程序員不難,但要做一個(gè)“程序匠”就不簡(jiǎn)單了。編程序很簡(jiǎn)單,但編出
有質(zhì)量的程序就難了。
?
?
我在這里不討論過深的技術(shù),我只想在一些容易讓人忽略的東西上說一說,雖然這些東西
可能很細(xì)微,但如果你不注意這些細(xì)微之處的話,那么他將會(huì)極大的影響你的整個(gè)軟件質(zhì)
量,以及整個(gè)軟件程的實(shí)施,所謂“千里之堤,毀于蟻穴”。
?
“細(xì)微之處見真功”,真正能體現(xiàn)一個(gè)程序的功底恰恰在這些細(xì)微之處。
?
這就是程序員的——編程修養(yǎng)。我總結(jié)了在用C/C++語(yǔ)言(主要是C語(yǔ)言)進(jìn)行程序?qū)懽魃?
的三十二個(gè)“修養(yǎng)”,通過這些,你可以寫出質(zhì)量高的程序,同時(shí)也會(huì)讓看你程序的人漬
漬稱道,那些看過你程序的人一定會(huì)說:“這個(gè)人的編程修養(yǎng)不錯(cuò)”。
?
??? ————————————————————————
?
??????? 01、版權(quán)和版本
??????? 02、縮進(jìn)、空格、換行、空行、對(duì)齊
??????? 03、程序注釋
??????? 04、函數(shù)的[in][out]參數(shù)
??????? 05、對(duì)系統(tǒng)調(diào)用的返回進(jìn)行判斷
??????? 06、if 語(yǔ)句對(duì)出錯(cuò)的處理
??????? 07、頭文件中的#ifndef
??????? 08、在堆上分配內(nèi)存
??????? 09、變量的初始化
??????? 10、h和c文件的使用
??????? 11、出錯(cuò)信息的處理
??????? 12、常用函數(shù)和循環(huán)語(yǔ)句中的被計(jì)算量
??????? 13、函數(shù)名和變量名的命名
??????? 14、函數(shù)的傳值和傳指針
??????? 15、修改別人程序的修養(yǎng)
??????? 16、把相同或近乎相同的代碼形成函數(shù)和宏
??????? 17、表達(dá)式中的括號(hào)
??????? 18、函數(shù)參數(shù)中的const
??????? 19、函數(shù)的參數(shù)個(gè)數(shù)
??????? 20、函數(shù)的返回類型,不要省略
??????? 21、goto語(yǔ)句的使用
??????? 22、宏的使用
??????? 23、static的使用
??????? 24、函數(shù)中的代碼尺寸
??????? 25、typedef的使用
??????? 26、為常量聲明宏
??????? 27、不要為宏定義加分號(hào)
??????? 28、||和&&的語(yǔ)句執(zhí)行順序
??????? 29、盡量用for而不是while做循環(huán)
??????? 30、請(qǐng)sizeof類型而不是變量
??????? 31、不要忽略Warning
??????? 32、書寫Debug版和Release版的程序
??????? 21、goto語(yǔ)究 使勁
??????? 22、宏的使用
??????? 23、static的使用
??????? 24、函數(shù)中的代碼尺寸
??????? 25、typedef的使用
??????? 26、為常量聲明宏
??????? 27、不要為宏定義加分號(hào)
??????? 28、||和&&的語(yǔ)句執(zhí)行順序
??????? 29、盡量用for而不是while做循環(huán)
??????? 30、請(qǐng)sizeof類型而不是變量
??????? 31、不要忽略Warning
??????? 32、書寫Debug版和Release版的程序
?
??? ————————————————————————
1、版權(quán)和版本
———————
好的程序員會(huì)給自己的每個(gè)函數(shù),每個(gè)文件,都注上版權(quán)和版本。
?
對(duì)于C/C++的文件,文件頭應(yīng)該有類似這樣的注釋:
/************************************************************************
*
*?? 文件名:network.c
*
*?? 文件描述:網(wǎng)絡(luò)通訊函數(shù)集
*
*?? 創(chuàng)建人: Hao Chen, 2003年2月3日
*
*?? 版本號(hào):1.0
*
*?? 修改記錄:
*
*
************************************************************************/
?
而對(duì)于函數(shù)來說,應(yīng)該也有類似于這樣的注釋:
?
/*================================================================
*
* 函 數(shù) 名:XXX
*
* 參??? 數(shù):
*
*??????? type name [IN] : descripts
*
* 功能描述:
*
*??????? ..............
*
* 返 回 值:成功TRUE,失敗FALSE
*
* 拋出異常:
*
* 作??? 者:ChenHao 2003/4/2
*
*
================================================================*/
?
這樣的描述可以讓人對(duì)一個(gè)函數(shù),一個(gè)文件有一個(gè)總體的認(rèn)識(shí),對(duì)代碼的易讀性和易維護(hù)
性有很大的好處。這是好的作品產(chǎn)生的開始。
?
?
?
2、縮進(jìn)、空格、換行、空行、對(duì)齊
————————————————
i) 縮進(jìn)應(yīng)該是每個(gè)程序都會(huì)做的,只要學(xué)程序過程序就應(yīng)該知道這個(gè),但是我仍然看過不
縮進(jìn)的程序,或是亂縮進(jìn)的程序,如果你的公司還有寫程序不縮進(jìn)的程序員,請(qǐng)毫不猶豫
的開除他吧,并以破壞源碼罪起訴他,還要他賠償讀過他程序的人的精神損失費(fèi)。縮進(jìn),
這是不成文規(guī)矩,我再重提一下吧,一個(gè)縮進(jìn)一般是一個(gè)TAB鍵或是4個(gè)空格。(最好用TAB
鍵)
?
ii) 空格。空格能給程序代來什么損失嗎?沒有,有效的利用空格可以讓你的程序讀進(jìn)來
更加賞心悅目。而不一堆表達(dá)式擠在一起。看看下面的代碼:
?
??? ha=(ha*128+*key++)%tabPtr->size;
?
??? ha = ( ha * 128 + *key++ ) % tabPtr->size;
?
?
??? 有空格和沒有空格的感覺不一樣吧。一般來說,語(yǔ)句中要在各個(gè)操作符間加空格,函
數(shù)調(diào)用時(shí),要以各個(gè)參數(shù)間加空格。如下面這種加空格的和不加的:
?
if ((hProc=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid))==NULL){
}
?
if ( ( hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid) ) == NULL ){
}
?
iii) 換行。不要把語(yǔ)句都寫在一行上,這樣很不好。如:
?
??? for(i=0;i<len;i++) if((a[i]<'0'||a[i]>'9')&&(a[i]<'a'||a[i]>'z')) break;
?
??? 我拷,這種即無空格,又無換行的程序在寫什么啊?加上空格和換行吧。
?
??? for ( i=0; i<len; i++) {
??????? if ( ( a[i] < '0' || a[i] > '9' ) &&
???????????? ( a[i] < 'a' || a[i] > 'z' ) ) {
??????????? break;
??????? }
??? }
?
?
??? 好多了吧?有時(shí)候,函數(shù)參數(shù)多的時(shí)候,最好也換行,如:
CreateProcess(
????????????????? NULL,
????????????????? cmdbuf,
????????????????? NULL,
????????????????? NULL,
????????????????? bInhH,
????????????????? dwCrtFlags,
????????????????? envbuf,
????????????????? NULL,
????????????????? &siStartInfo,
????????????????? &prInfo
???????????????? );
?
??? 條件語(yǔ)句也應(yīng)該在必要時(shí)換行:
?
??? if ( ch >= '0' || ch <= '9' ||
???????? ch >= 'a' || ch <= 'z' ||
???????? ch >= 'A' || ch <= 'Z' )
?
?
iv) 空行。不要不加空行,空行可以區(qū)分不同的程序塊,程序塊間,最好加上空行。如:
??????? 12、常用函數(shù)和循環(huán)語(yǔ)句中的被計(jì)算量
??????? 13、函數(shù)名和變量名的命名
??????? 14、函數(shù)的傳值和傳指針
??????? 15、修改別人程序的修養(yǎng)
??????? 16、把相同或近乎相同的代碼形成函數(shù)和宏
??????? 17、表達(dá)式中的括號(hào)
??????? 18、函數(shù)參數(shù)中的const
??????? 19、函數(shù)的參數(shù)個(gè)數(shù)
??????? 20、函數(shù)的返回類型,不要省略
??????? 21、goto語(yǔ)句的使用
??????? 22、宏的使用
??????? 23、static的使用
??????? 24、函數(shù)中的代碼尺寸
??????? 25、typedef的使用
??????? 26、為常量聲明宏
??????? 27、不要為宏定義加分號(hào)
??????? 28、||和&&的語(yǔ)句執(zhí)行順序
??????? 29、盡量用for而不是while做循環(huán)
??????? 30、請(qǐng)sizeof類型而不是變量
??????? 31、不要忽略Warning
??????? 32、書寫Debug版和Release版的程序
??????? 21、goto語(yǔ)究 使勁
??????? 22、宏的使用
??????? 23、static的使用
??????? 24、函數(shù)中的代碼尺寸
??????? 25、typedef的使用
??????? 26、為常量聲明宏
??????? 27、不要為宏定義加分號(hào)
??????? 28、||和&&的語(yǔ)句執(zhí)行順序
??????? 29、盡量用for而不是while做循環(huán)
??????? 30、請(qǐng)sizeof類型而不是變量
??????? 31、不要忽略Warning
??????? 32、書寫Debug版和Release版的程序
?
??? ————————————————————————
1、版權(quán)和版本
———————
好的程序員會(huì)給自己的每個(gè)函數(shù),每個(gè)文件,都注上版權(quán)和版本。
?
對(duì)于C/C++的文件,文件頭應(yīng)該有類似這樣的注釋:
/************************************************************************
*
*?? 文件名:network.c
*
*?? 文件描述:網(wǎng)絡(luò)通訊函數(shù)集
*
*?? 創(chuàng)建人: Hao Chen, 2003年2月3日
*
*?? 版本號(hào):1.0
*
*?? 修改記錄:
*
*
************************************************************************/
?
而對(duì)于函數(shù)來說,應(yīng)該也有類似于這樣的注釋:
?
/*================================================================
*
* 函 數(shù) 名:XXX
*
* 參??? 數(shù):
*
*??????? type name [IN] : descripts
*
* 功能描述:
*
*??????? ..............
*
* 返 回 值:成功TRUE,失敗FALSE
*
* 拋出異常:
*
* 作??? 者:ChenHao 2003/4/2
*
*
================================================================*/
?
這樣的描述可以讓人對(duì)一個(gè)函數(shù),一個(gè)文件有一個(gè)總體的認(rèn)識(shí),對(duì)代碼的易讀性和易維護(hù)
性有很大的好處。這是好的作品產(chǎn)生的開始。
?
?
?
2、縮進(jìn)、空格、換行、空行、對(duì)齊
————————————————
i) 縮進(jìn)應(yīng)該是每個(gè)程序都會(huì)做的,只要學(xué)程序過程序就應(yīng)該知道這個(gè),但是我仍然看過不
縮進(jìn)的程序,或是亂縮進(jìn)的程序,如果你的公司還有寫程序不縮進(jìn)的程序員,請(qǐng)毫不猶豫
的開除他吧,并以破壞源碼罪起訴他,還要他賠償讀過他程序的人的精神損失費(fèi)。縮進(jìn),
這是不成文規(guī)矩,我再重提一下吧,一個(gè)縮進(jìn)一般是一個(gè)TAB鍵或是4個(gè)空格。(最好用TAB
鍵)
?
ii) 空格。空格能給程序代來什么損失嗎?沒有,有效的利用空格可以讓你的程序讀進(jìn)來
更加賞心悅目。而不一堆表達(dá)式擠在一起。看看下面的代碼:
?
??? ha=(ha*128+*key++)%tabPtr->size;
?
??? ha = ( ha * 128 + *key++ ) % tabPtr->size;
?
?
??? 有空格和沒有空格的感覺不一樣吧。一般來說,語(yǔ)句中要在各個(gè)操作符間加空格,函
數(shù)調(diào)用時(shí),要以各個(gè)參數(shù)間加空格。如下面這種加空格的和不加的:
?
if ((hProc=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid))==NULL){
}
?
if ( ( hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid) ) == NULL ){
}
?
iii) 換行。不要把語(yǔ)句都寫在一行上,這樣很不好。如:
?
??? for(i=0;i<len;i++) if((a[i]<'0'||a[i]>'9')&&(a[i]<'a'||a[i]>'z')) break;
?
??? 我拷,這種即無空格,又無換行的程序在寫什么啊?加上空格和換行吧。
?
??? for ( i=0; i<len; i++) {
??????? if ( ( a[i] < '0' || a[i] > '9' ) &&
???????????? ( a[i] < 'a' || a[i] > 'z' ) ) {
??????????? break;
??????? }
??? }
?
?
??? 好多了吧?有時(shí)候,函數(shù)參數(shù)多的時(shí)候,最好也換行,如:
CreateProcess(
????????????????? NULL,
????????????????? cmdbuf,
????????????????? NULL,
????????????????? NULL,
????????????????? bInhH,
????????????????? dwCrtFlags,
????????????????? envbuf,
????????????????? NULL,
????????????????? &siStartInfo,
????????????????? &prInfo
???????????????? );
?
??? 條件語(yǔ)句也應(yīng)該在必要時(shí)換行:
?
??? if ( ch >= '0' || ch <= '9' ||
???????? ch >= 'a' || ch <= 'z' ||
???????? ch >= 'A' || ch <= 'Z' )
?
?
iv) 空行。不要不加空行,空行可以區(qū)分不同的程序塊,程序塊間,最好加上空行。如:
??? HANDLE hProcess;
??? PROCESS_T procInfo;
?
??? /* open the process handle */
??? if((hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid)) == NULL)
??? {
??????? return LSE_MISC_SYS;
??? }
?
??? memset(&procInfo, 0, sizeof(procInfo));
??? procInfo.idProc = pid;
??? procInfo.hdProc = hProcess;
??? procInfo.misc |= MSCAVA_PROC;
?
??? return(0);
?
v) 對(duì)齊。用TAB鍵對(duì)齊你的一些變量的聲明或注釋,一樣會(huì)讓你的程序好看一些。如:
?
typedef struct _pt_man_t_ {
??? int???? numProc;??? /* Number of processes???????????????? */
??? int???? maxProc;??? /* Max Number of processes???????????? */
??? int???? maxProc;??? /* Max Number of processes???????????? */
??? int???? numEvnt;??? /* Number of events??????????????????? */
??? int???? maxEvnt;??? /* Max Number of events??????????????? */
??? HANDLE* pHndEvnt;?? /* Array of events???????????????????? */
??? DWORD?? timeout;??? /* Time out interval?????????????????? */
??? HANDLE? hPipe;????? /* Namedpipe?????????????????????????? */
??? TCHAR?? usr[MAXUSR];/* User name of the process??????????? */
??? int???? numMsg;???? /* Number of Message?????????????????? */
??? int???? Msg[MAXMSG];/* Space for intro process communicate */
} PT_MAN_T;
?
怎么樣?感覺不錯(cuò)吧。
?
這里主要講述了如果寫出讓人賞心悅目的代碼,好看的代碼會(huì)讓人的心情愉快,讀起代碼
也就不累,工整、整潔的程序代碼,通常更讓人歡迎,也更讓人稱道。現(xiàn)在的硬盤空間這
么大,不要讓你的代碼擠在一起,這樣它們會(huì)抱怨你虐待它們的。好了,用“縮進(jìn)、空格
、換行、空行、對(duì)齊”裝飾你的代碼吧,讓他們從沒有秩序的土匪中變成一排排整齊有秩
序的正規(guī)部隊(duì)吧。
?
?
?
?
3、程序注釋
3、程序注釋
——————
養(yǎng)成寫程序注釋的習(xí)慣,這是每個(gè)程序員所必須要做的工作。我看過那種幾千行,卻居然
沒有一行注釋的程序。這就如同在公路上駕車卻沒有路標(biāo)一樣。用不了多久,連自己都不
知道自己的意圖了,還要花上幾倍的時(shí)間才看明白,這種浪費(fèi)別人和自己的時(shí)間的人,是
最為可恥的人。
?
是的,你也許會(huì)說,你會(huì)寫注釋,真的嗎?注釋的書寫也能看出一個(gè)程序員的功底。一般
來說你需要至少寫這些地方的注釋:文件的注釋、函數(shù)的注釋、變量的注釋、算法的注釋
、功能塊的程序注釋。主要就是記錄你這段程序是干什么的?你的意圖是什么?你這個(gè)變
量是用來做什么的?等等。
?
不要以為注釋好寫,有一些算法是很難說或?qū)懗鰜淼模荒芤鈺?huì),我承認(rèn)有這種情況的時(shí)
候,但你也要寫出來,正好可以訓(xùn)練一下自己的表達(dá)能力。而表達(dá)能力正是那種悶頭搞技
術(shù)的技術(shù)人員最缺的,你有再高的技術(shù),如果你表達(dá)能力不行,你的技術(shù)將不能得到充分
的發(fā)揮。因?yàn)椋@是一個(gè)團(tuán)隊(duì)的時(shí)代。
?
好了,說幾個(gè)注釋的技術(shù)細(xì)節(jié):
?
i) 對(duì)于行注釋(“//”)比塊注釋(“/* */”)要好的說法,我并不是很同意。因?yàn)橐?
些老版本的C編譯器并不支持行注釋,所以為了你的程序的移植性,請(qǐng)你還是盡量使用塊注
釋。
?
?
ii) 你也許會(huì)為塊注釋的不能嵌套而不爽,那么你可以用預(yù)編譯來完成這個(gè)功能。使用“#
if 0”和“#endif”括起來的代碼,將不被編譯,而且還可以嵌套。
?
?
?
?
4、函數(shù)的[in][out]參數(shù)
———————————
?
我經(jīng)常看到這樣的程序:
FuncName(char* str)
{
??? int len = strlen(str);
??? .....
}
?
char*
GetUserName(struct user* pUser)
{
??? return pUser->name;
}
?
?
不!請(qǐng)不要這樣做。
你應(yīng)該先判斷一下傳進(jìn)來的那個(gè)指針是不是為空。如果傳進(jìn)來的指針為空的話,那么,你
的一個(gè)大的系統(tǒng)就會(huì)因?yàn)檫@一個(gè)小的函數(shù)而崩潰。一種更好的技術(shù)是使用斷言(assert)
,這里我就不多說這些技術(shù)細(xì)節(jié)了。當(dāng)然,如果是在C++中,引用要比指針好得多,但你也
需要對(duì)各個(gè)參數(shù)進(jìn)行檢查。
?
寫有參數(shù)的函數(shù)時(shí),首要工作,就是要對(duì)傳進(jìn)來的所有參數(shù)進(jìn)行合法性檢查。而對(duì)于傳出
的參數(shù)也應(yīng)該進(jìn)行檢查,這個(gè)動(dòng)作當(dāng)然應(yīng)該在函數(shù)的外部,也就是說,調(diào)用完一個(gè)函數(shù)后
,應(yīng)該對(duì)其傳出的值進(jìn)行檢查。
?
當(dāng)然,檢查會(huì)浪費(fèi)一點(diǎn)時(shí)間,但為了整個(gè)系統(tǒng)不至于出現(xiàn)“非法操作”或是“Core Dump”
的系統(tǒng)級(jí)的錯(cuò)誤,多花這點(diǎn)時(shí)間還是很值得的。
???
5、對(duì)系統(tǒng)調(diào)用的返回進(jìn)行判斷
——————————————
繼續(xù)上一條,對(duì)于一些系統(tǒng)調(diào)用,比如打開文件,我經(jīng)常看到,許多程序員對(duì)fopen返回的
指針不做任何判斷,就直接使用了。然后發(fā)現(xiàn)文件的內(nèi)容怎么也讀出不,或是怎么也寫不
進(jìn)去。還是判斷一下吧:
?
?
??? fp = fopen("log.txt", "a");
??? if ( fp == NULL ){
??????? printf("Error: open file error\n");
??????? return FALSE;
??? }
?
其它還有許多啦,比如:socket返回的socket號(hào),malloc返回的內(nèi)存。請(qǐng)對(duì)這些系統(tǒng)調(diào)用
返回的東西進(jìn)行判斷。
6、if 語(yǔ)句對(duì)出錯(cuò)的處理
———————————
我看見你說了,這有什么好說的。還是先看一段程序代碼吧。
?
??? if ( ch >= '0' && ch <= '9' ){
??????? /* 正常處理代碼 */
??? }else{
??????? /* 輸出錯(cuò)誤信息 */
??????? printf("error ......\n");
??????? return ( FALSE );
??? }
?
這種結(jié)構(gòu)很不好,特別是如果“正常處理代碼”很長(zhǎng)時(shí),對(duì)于這種情況,最好不要用else
。先判斷錯(cuò)誤,如:
?
??? if ( ch < '0' || ch > '9' ){
??????? /* 輸出錯(cuò)誤信息 */
??????? printf("error ......\n");
??????? return ( FALSE );
??? }
?
??? /* 正常處理代碼 */
??? ......
?
?
這樣的結(jié)構(gòu),不是很清楚嗎?突出了錯(cuò)誤的條件,讓別人在使用你的函數(shù)的時(shí)候,第一眼
就能看到不合法的條件,于是就會(huì)更下意識(shí)的避免。
?
?
?
?
7、頭文件中的#ifndef
——————————
千萬不要忽略了頭件的中的#ifndef,這是一個(gè)很關(guān)鍵的東西。比如你有兩個(gè)C文件,這兩
個(gè)C文件都include了同一個(gè)頭文件。而編譯時(shí),這兩個(gè)C文件要一同編譯成一個(gè)可運(yùn)行文件
,于是問題來了,大量的聲明沖突。
?
還是把頭文件的內(nèi)容都放在#ifndef和#endif中吧。不管你的頭文件會(huì)不會(huì)被多個(gè)文件引用
管你的頭文件會(huì)不會(huì)被多個(gè)文件引用
,你都要加上這個(gè)。一般格式是這樣的:
?
??? #ifndef? <標(biāo)識(shí)>
??? #define <標(biāo)識(shí)>
?
??? ......
??? ......
?
??? #endif
?
<標(biāo)識(shí)>在理論上來說可以是自由命名的,但每個(gè)頭文件的這個(gè)“標(biāo)識(shí)”都應(yīng)該是唯一的。
標(biāo)識(shí)的命名規(guī)則一般是頭文件名全大寫,前后加下劃線,并把文件名中的“.”也變成下劃
線,如:stdio.h
?
??? #ifndef _STDIO_H_
??? #define _STDIO_H_
?
??? ......
?
??? #endif
?
(BTW:預(yù)編譯有多很有用的功能。你會(huì)用預(yù)編譯嗎?)
(BTW:預(yù)編譯有多很有用的功能。你會(huì)用預(yù)編譯嗎?)
?
?
?
?
8、在堆上分配內(nèi)存
—————————
可能許多人對(duì)內(nèi)存分配上的“棧 stack”和“堆 heap”還不是很明白。包括一些科班出身
的人也不明白這兩個(gè)概念。我不想過多的說這兩個(gè)東西。簡(jiǎn)單的來講,stack上分配的內(nèi)存
系統(tǒng)自動(dòng)釋放,heap上分配的內(nèi)存,系統(tǒng)不釋放,哪怕程序退出,那一塊內(nèi)存還是在那里
。stack一般是靜態(tài)分配內(nèi)存,heap上一般是動(dòng)態(tài)分配內(nèi)存。
?
由malloc系統(tǒng)函數(shù)分配的內(nèi)存就是從堆上分配內(nèi)存。從堆上分配的內(nèi)存一定要自己釋放。
用free釋放,不然就是術(shù)語(yǔ)——“內(nèi)存泄露”(或是“內(nèi)存漏洞”)—— Memory Leak。
于是,系統(tǒng)的可分配內(nèi)存會(huì)隨malloc越來越少,直到系統(tǒng)崩潰。還是來看看“棧內(nèi)存”和
“堆內(nèi)存”的差別吧。
?
??? 棧內(nèi)存分配
??? —————
??? char*
??? AllocStrFromStack()
??? {
??????? char pstr[100];
??????? printf("error ......\n");
??????? return ( FALSE );
??? }
?
??? /* 正常處理代碼 */
??? ......
?
?
這樣的結(jié)構(gòu),不是很清楚嗎?突出了錯(cuò)誤的條件,讓別人在使用你的函數(shù)的時(shí)候,第一眼
就能看到不合法的條件,于是就會(huì)更下意識(shí)的避免。
?
?
?
?
7、頭文件中的#ifndef
——————————
千萬不要忽略了頭件的中的#ifndef,這是一個(gè)很關(guān)鍵的東西。比如你有兩個(gè)C文件,這兩
個(gè)C文件都include了同一個(gè)頭文件。而編譯時(shí),這兩個(gè)C文件要一同編譯成一個(gè)可運(yùn)行文件
,于是問題來了,大量的聲明沖突。
?
還是把頭文件的內(nèi)容都放在#ifndef和#endif中吧。不管你的頭文件會(huì)不會(huì)被多個(gè)文件引用
管你的頭文件會(huì)不會(huì)被多個(gè)文件引用
,你都要加上這個(gè)。一般格式是這樣的:
?
??? #ifndef? <標(biāo)識(shí)>
??? #define <標(biāo)識(shí)>
?
??? ......
??? ......
?
??? #endif
?
<標(biāo)識(shí)>在理論上來說可以是自由命名的,但每個(gè)頭文件的這個(gè)“標(biāo)識(shí)”都應(yīng)該是唯一的。
標(biāo)識(shí)的命名規(guī)則一般是頭文件名全大寫,前后加下劃線,并把文件名中的“.”也變成下劃
線,如:stdio.h
?
??? #ifndef _STDIO_H_
??? #define _STDIO_H_
?
??? ......
?
??? #endif
?
(BTW:預(yù)編譯有多很有用的功能。你會(huì)用預(yù)編譯嗎?)
(BTW:預(yù)編譯有多很有用的功能。你會(huì)用預(yù)編譯嗎?)
?
?
?
?
8、在堆上分配內(nèi)存
—————————
可能許多人對(duì)內(nèi)存分配上的“棧 stack”和“堆 heap”還不是很明白。包括一些科班出身
的人也不明白這兩個(gè)概念。我不想過多的說這兩個(gè)東西。簡(jiǎn)單的來講,stack上分配的內(nèi)存
系統(tǒng)自動(dòng)釋放,heap上分配的內(nèi)存,系統(tǒng)不釋放,哪怕程序退出,那一塊內(nèi)存還是在那里
。stack一般是靜態(tài)分配內(nèi)存,heap上一般是動(dòng)態(tài)分配內(nèi)存。
?
由malloc系統(tǒng)函數(shù)分配的內(nèi)存就是從堆上分配內(nèi)存。從堆上分配的內(nèi)存一定要自己釋放。
用free釋放,不然就是術(shù)語(yǔ)——“內(nèi)存泄露”(或是“內(nèi)存漏洞”)—— Memory Leak。
于是,系統(tǒng)的可分配內(nèi)存會(huì)隨malloc越來越少,直到系統(tǒng)崩潰。還是來看看“棧內(nèi)存”和
“堆內(nèi)存”的差別吧。
?
??? 棧內(nèi)存分配
??? —————
??? char*
??? AllocStrFromStack()
??? {
??????? char pstr[100];
??????? return pstr;
??? }
?
?
??? 堆內(nèi)存分配
??? —————
??? char*
??? AllocStrFromHeap(int len)
??? {
??????? char *pstr;
?
??????? if ( len <= 0 ) return NULL;
??????? return ( char* ) malloc( len );
??? }
?
對(duì)于第一個(gè)函數(shù),那塊pstr的內(nèi)存在函數(shù)返回時(shí)就被系統(tǒng)釋放了。于是所返回的char*什么
也沒有。而對(duì)于第二個(gè)函數(shù),是從堆上分配內(nèi)存,所以哪怕是程序退出時(shí),也不釋放,所
以第二個(gè)函數(shù)的返回的內(nèi)存沒有問題,可以被使用。但一定要調(diào)用free釋放,不然就是Mem
ory Leak!
?
在堆上分配內(nèi)存很容易造成內(nèi)存泄漏,這是C/C++的最大的“克星”,如果你的程序要穩(wěn)定
,那么就不要出現(xiàn)Memory Leak。所以,我還是要在這里千叮嚀萬囑付,在使用malloc系統(tǒng)
蛑齦叮謔褂胢alloc系統(tǒng)
函數(shù)(包括calloc,realloc)時(shí)千萬要小心。
?
記得有一個(gè)UNIX上的服務(wù)應(yīng)用程序,大約有幾百的C文件編譯而成,運(yùn)行測(cè)試良好,等使用
時(shí),每隔三個(gè)月系統(tǒng)就是down一次,搞得許多人焦頭爛額,查不出問題所在。只好,每隔
兩個(gè)月人工手動(dòng)重啟系統(tǒng)一次。出現(xiàn)這種問題就是Memery Leak在做怪了,在C/C++中這種
問題總是會(huì)發(fā)生,所以你一定要小心。一個(gè)Rational的檢測(cè)工作——Purify,可以幫你測(cè)
試你的程序有沒有內(nèi)存泄漏。
?
我保證,做過許多C/C++的工程的程序員,都會(huì)對(duì)malloc或是new有些感冒。當(dāng)你什么時(shí)候
在使用malloc和new時(shí),有一種輕度的緊張和惶恐的感覺時(shí),你就具備了這方面的修養(yǎng)了。
?
對(duì)于malloc和free的操作有以下規(guī)則:
?
1) 配對(duì)使用,有一個(gè)malloc,就應(yīng)該有一個(gè)free。(C++中對(duì)應(yīng)為new和delete)
2) 盡量在同一層上使用,不要像上面那種,malloc在函數(shù)中,而free在函數(shù)外。最好在同
一調(diào)用層上使用這兩個(gè)函數(shù)。
3) malloc分配的內(nèi)存一定要初始化。free后的指針一定要設(shè)置為NULL。
?
注:雖然現(xiàn)在的操作系統(tǒng)(如:UNIX和Win2k/NT)都有進(jìn)程內(nèi)存跟蹤機(jī)制,也就是如果你
有沒有釋放的內(nèi)存,操作系統(tǒng)會(huì)幫你釋放。但操作系統(tǒng)依然不會(huì)釋放你程序中所有產(chǎn)生了M
emory Leak的內(nèi)存,所以,最好還是你自己來做這個(gè)工作。(有的時(shí)候不知不覺就出現(xiàn)Mem
ory Leak了,而且在幾百萬行的代碼中找無異于海底撈針,Rational有一個(gè)工具叫Purify
蛐械拇脛姓椅摶煊諍5桌陶耄琑ational有一個(gè)工具叫Purify
,可能很好的幫你檢查程序中的Memory Leak)
?
?
?
9、變量的初始化
————————
接上一條,變量一定要被初始化再使用。C/C++編譯器在這個(gè)方面不會(huì)像JAVA一樣幫你初始
化,這一切都需要你自己來,如果你使用了沒有初始化的變量,結(jié)果未知。好的程序員從
來都會(huì)在使用變量前初始化變量的。如:
?
??? 1) 對(duì)malloc分配的內(nèi)存進(jìn)行memset清零操作。(可以使用calloc分配一塊全零的內(nèi)存
)
??? 2) 對(duì)一些棧上分配的struct或數(shù)組進(jìn)行初始化。(最好也是清零)
?
不過話又說回來了,初始化也會(huì)造成系統(tǒng)運(yùn)行時(shí)間有一定的開銷,所以,也不要對(duì)所有的
變量做初始化,這個(gè)也沒有意義。好的程序員知道哪些變量需要初始化,哪些則不需要。
如:以下這種情況,則不需要。
?
??????? char *pstr;? /* 一個(gè)字符串 */
??????? pstr = ( char* ) malloc( 50 );
??????? if ( pstr == NULL ) exit(0);
??????? strcpy( pstr, "Hello Wrold" );
??????? strcpy( pstr, "Hello Wrold" );
?
但如果是下面一種情況,最好進(jìn)行內(nèi)存初始化。(指針是一個(gè)危險(xiǎn)的東西,一定要初始化
)
?
??????? char **pstr;? /* 一個(gè)字符串?dāng)?shù)組 */
??????? pstr = ( char** ) malloc( 50 );
??????? if ( pstr == NULL ) exit(0);
?
??????? /* 讓數(shù)組中的指針都指向NULL */
??????? memset( pstr, 0, 50*sizeof(char*) );
?
而對(duì)于全局變量,和靜態(tài)變量,一定要聲明時(shí)就初始化。因?yàn)槟悴恢浪谝淮螘?huì)在哪里
被使用。所以使用前初始這些變量是比較不現(xiàn)實(shí)的,一定要在聲明時(shí)就初始化它們。如:
?
??? Links *plnk = NULL;? /* 對(duì)于全局變量plnk初始化為NULL */
?
?
?
?
?
10、h和c文件的使用
—————————
—————————
H文件和C文件怎么用呢?一般來說,H文件中是declare(聲明),C文件中是define(定義
)。因?yàn)镃文件要編譯成庫(kù)文件(Windows下是.obj/.lib,UNIX下是.o/.a),如果別人要
使用你的函數(shù),那么就要引用你的H文件,所以,H文件中一般是變量、宏定義、枚舉、結(jié)
構(gòu)和函數(shù)接口的聲明,就像一個(gè)接口說明文件一樣。而C文件則是實(shí)現(xiàn)細(xì)節(jié)。
?
H文件和C文件最大的用處就是聲明和實(shí)現(xiàn)分開。這個(gè)特性應(yīng)該是公認(rèn)的了,但我仍然看到
有些人喜歡把函數(shù)寫在H文件中,這種習(xí)慣很不好。(如果是C++話,對(duì)于其模板函數(shù),在V
C中只有把實(shí)現(xiàn)和聲明都寫在一個(gè)文件中,因?yàn)閂C不支持export關(guān)鍵字)。而且,如果在H
文件中寫上函數(shù)的實(shí)現(xiàn),你還得在makefile中把頭文件的依賴關(guān)系也加上去,這個(gè)就會(huì)讓
你的makefile很不規(guī)范。
?
最后,有一個(gè)最需要注意的地方就是:帶初始化的全局變量不要放在H文件中!
?
例如有一個(gè)處理錯(cuò)誤信息的結(jié)構(gòu):
?
char* errmsg[] = {
??????? /* 0 */?????? "No error",
??????? /* 1 */?????? "Open file error",
??????? /* 2 */?????? "Failed in sending/receiving a message",
??????? /* 3 */?????? "Bad arguments",
??????? /* 4 */?????? "Memeroy is not enough",
??????? /* 5 */?????? "Service is down; try later",
??? }
?
?
??? 堆內(nèi)存分配
??? —————
??? char*
??? AllocStrFromHeap(int len)
??? {
??????? char *pstr;
?
??????? if ( len <= 0 ) return NULL;
??????? return ( char* ) malloc( len );
??? }
?
對(duì)于第一個(gè)函數(shù),那塊pstr的內(nèi)存在函數(shù)返回時(shí)就被系統(tǒng)釋放了。于是所返回的char*什么
也沒有。而對(duì)于第二個(gè)函數(shù),是從堆上分配內(nèi)存,所以哪怕是程序退出時(shí),也不釋放,所
以第二個(gè)函數(shù)的返回的內(nèi)存沒有問題,可以被使用。但一定要調(diào)用free釋放,不然就是Mem
ory Leak!
?
在堆上分配內(nèi)存很容易造成內(nèi)存泄漏,這是C/C++的最大的“克星”,如果你的程序要穩(wěn)定
,那么就不要出現(xiàn)Memory Leak。所以,我還是要在這里千叮嚀萬囑付,在使用malloc系統(tǒng)
蛑齦叮謔褂胢alloc系統(tǒng)
函數(shù)(包括calloc,realloc)時(shí)千萬要小心。
?
記得有一個(gè)UNIX上的服務(wù)應(yīng)用程序,大約有幾百的C文件編譯而成,運(yùn)行測(cè)試良好,等使用
時(shí),每隔三個(gè)月系統(tǒng)就是down一次,搞得許多人焦頭爛額,查不出問題所在。只好,每隔
兩個(gè)月人工手動(dòng)重啟系統(tǒng)一次。出現(xiàn)這種問題就是Memery Leak在做怪了,在C/C++中這種
問題總是會(huì)發(fā)生,所以你一定要小心。一個(gè)Rational的檢測(cè)工作——Purify,可以幫你測(cè)
試你的程序有沒有內(nèi)存泄漏。
?
我保證,做過許多C/C++的工程的程序員,都會(huì)對(duì)malloc或是new有些感冒。當(dāng)你什么時(shí)候
在使用malloc和new時(shí),有一種輕度的緊張和惶恐的感覺時(shí),你就具備了這方面的修養(yǎng)了。
?
對(duì)于malloc和free的操作有以下規(guī)則:
?
1) 配對(duì)使用,有一個(gè)malloc,就應(yīng)該有一個(gè)free。(C++中對(duì)應(yīng)為new和delete)
2) 盡量在同一層上使用,不要像上面那種,malloc在函數(shù)中,而free在函數(shù)外。最好在同
一調(diào)用層上使用這兩個(gè)函數(shù)。
3) malloc分配的內(nèi)存一定要初始化。free后的指針一定要設(shè)置為NULL。
?
注:雖然現(xiàn)在的操作系統(tǒng)(如:UNIX和Win2k/NT)都有進(jìn)程內(nèi)存跟蹤機(jī)制,也就是如果你
有沒有釋放的內(nèi)存,操作系統(tǒng)會(huì)幫你釋放。但操作系統(tǒng)依然不會(huì)釋放你程序中所有產(chǎn)生了M
emory Leak的內(nèi)存,所以,最好還是你自己來做這個(gè)工作。(有的時(shí)候不知不覺就出現(xiàn)Mem
ory Leak了,而且在幾百萬行的代碼中找無異于海底撈針,Rational有一個(gè)工具叫Purify
蛐械拇脛姓椅摶煊諍5桌陶耄琑ational有一個(gè)工具叫Purify
,可能很好的幫你檢查程序中的Memory Leak)
?
?
?
9、變量的初始化
————————
接上一條,變量一定要被初始化再使用。C/C++編譯器在這個(gè)方面不會(huì)像JAVA一樣幫你初始
化,這一切都需要你自己來,如果你使用了沒有初始化的變量,結(jié)果未知。好的程序員從
來都會(huì)在使用變量前初始化變量的。如:
?
??? 1) 對(duì)malloc分配的內(nèi)存進(jìn)行memset清零操作。(可以使用calloc分配一塊全零的內(nèi)存
)
??? 2) 對(duì)一些棧上分配的struct或數(shù)組進(jìn)行初始化。(最好也是清零)
?
不過話又說回來了,初始化也會(huì)造成系統(tǒng)運(yùn)行時(shí)間有一定的開銷,所以,也不要對(duì)所有的
變量做初始化,這個(gè)也沒有意義。好的程序員知道哪些變量需要初始化,哪些則不需要。
如:以下這種情況,則不需要。
?
??????? char *pstr;? /* 一個(gè)字符串 */
??????? pstr = ( char* ) malloc( 50 );
??????? if ( pstr == NULL ) exit(0);
??????? strcpy( pstr, "Hello Wrold" );
??????? strcpy( pstr, "Hello Wrold" );
?
但如果是下面一種情況,最好進(jìn)行內(nèi)存初始化。(指針是一個(gè)危險(xiǎn)的東西,一定要初始化
)
?
??????? char **pstr;? /* 一個(gè)字符串?dāng)?shù)組 */
??????? pstr = ( char** ) malloc( 50 );
??????? if ( pstr == NULL ) exit(0);
?
??????? /* 讓數(shù)組中的指針都指向NULL */
??????? memset( pstr, 0, 50*sizeof(char*) );
?
而對(duì)于全局變量,和靜態(tài)變量,一定要聲明時(shí)就初始化。因?yàn)槟悴恢浪谝淮螘?huì)在哪里
被使用。所以使用前初始這些變量是比較不現(xiàn)實(shí)的,一定要在聲明時(shí)就初始化它們。如:
?
??? Links *plnk = NULL;? /* 對(duì)于全局變量plnk初始化為NULL */
?
?
?
?
?
10、h和c文件的使用
—————————
—————————
H文件和C文件怎么用呢?一般來說,H文件中是declare(聲明),C文件中是define(定義
)。因?yàn)镃文件要編譯成庫(kù)文件(Windows下是.obj/.lib,UNIX下是.o/.a),如果別人要
使用你的函數(shù),那么就要引用你的H文件,所以,H文件中一般是變量、宏定義、枚舉、結(jié)
構(gòu)和函數(shù)接口的聲明,就像一個(gè)接口說明文件一樣。而C文件則是實(shí)現(xiàn)細(xì)節(jié)。
?
H文件和C文件最大的用處就是聲明和實(shí)現(xiàn)分開。這個(gè)特性應(yīng)該是公認(rèn)的了,但我仍然看到
有些人喜歡把函數(shù)寫在H文件中,這種習(xí)慣很不好。(如果是C++話,對(duì)于其模板函數(shù),在V
C中只有把實(shí)現(xiàn)和聲明都寫在一個(gè)文件中,因?yàn)閂C不支持export關(guān)鍵字)。而且,如果在H
文件中寫上函數(shù)的實(shí)現(xiàn),你還得在makefile中把頭文件的依賴關(guān)系也加上去,這個(gè)就會(huì)讓
你的makefile很不規(guī)范。
?
最后,有一個(gè)最需要注意的地方就是:帶初始化的全局變量不要放在H文件中!
?
例如有一個(gè)處理錯(cuò)誤信息的結(jié)構(gòu):
?
char* errmsg[] = {
??????? /* 0 */?????? "No error",
??????? /* 1 */?????? "Open file error",
??????? /* 2 */?????? "Failed in sending/receiving a message",
??????? /* 3 */?????? "Bad arguments",
??????? /* 4 */?????? "Memeroy is not enough",
??????? /* 5 */?????? "Service is down; try later",
??????? /* 6 */?????? "Unknow information",
??????? /* 7 */?????? "A socket operation has failed",
??????? /* 8 */?????? "Permission denied",
??????? /* 9 */?????? "Bad configuration file format",
??????? /* 10 */????? "Communication time out",
??????? ......
??????? ......
??? };
?
請(qǐng)不要把這個(gè)東西放在頭文件中,因?yàn)槿绻愕倪@個(gè)頭文件被5個(gè)函數(shù)庫(kù)(.lib或是.a)所
用到,于是他就被鏈接在這5個(gè).lib或.a中,而如果你的一個(gè)程序用到了這5個(gè)函數(shù)庫(kù)中的
函數(shù),并且這些函數(shù)都用到了這個(gè)出錯(cuò)信息數(shù)組。那么這份信息將有5個(gè)副本存在于你的執(zhí)
行文件中。如果你的這個(gè)errmsg很大的話,而且你用到的函數(shù)庫(kù)更多的話,你的執(zhí)行文件
也會(huì)變得很大。
?
正確的寫法應(yīng)該把它寫到C文件中,然后在各個(gè)需要用到errmsg的C文件頭上加上 extern
char* errmsg[]; 的外部聲明,讓編譯器在鏈接時(shí)才去管他,這樣一來,就只會(huì)有一個(gè)err
msg存在于執(zhí)行文件中,而且,這樣做很利于封裝。
?
我曾遇到過的最瘋狂的事,就是在我的目標(biāo)文件中,這個(gè)errmsg一共有112個(gè)副本,執(zhí)行文
件有8M左右。當(dāng)我把errmsg放到C文件中,并為一千多個(gè)C文件加上了extern的聲明后,所
有的函數(shù)庫(kù)文件尺寸都下降了20%左右,而我的執(zhí)行文件只有5M了。一下子少了3M啊。
?
[ 備注 ]
—————
有朋友對(duì)我說,這個(gè)只是一個(gè)特例,因?yàn)椋绻鹐rrmsg在執(zhí)行文件中存在多個(gè)副本時(shí),可
以加快程序運(yùn)行速度,理由是errmsg的多個(gè)復(fù)本會(huì)讓系統(tǒng)的內(nèi)存換頁(yè)降低,達(dá)到效率提升
。像我們這里所說的errmsg只有一份,當(dāng)某函數(shù)要用errmsg時(shí),如果內(nèi)存隔得比較遠(yuǎn),會(huì)
產(chǎn)生換頁(yè),反而效率不高。
?
?
生副本導(dǎo)致執(zhí)行文件尺寸變大,不僅增加了系統(tǒng)裝載時(shí)間,也會(huì)讓一個(gè)程序在內(nèi)存中占更
多的頁(yè)面。而對(duì)于errmsg這樣數(shù)據(jù),一般來說,在系統(tǒng)運(yùn)行時(shí)不會(huì)經(jīng)常用到,所以還是產(chǎn)
生的內(nèi)存換頁(yè)也就不算頻繁。權(quán)衡之下,還是只有一份errmsg的效率高。即便是像logmsg
這樣頻繁使用的的數(shù)據(jù),操作系統(tǒng)的內(nèi)存調(diào)度算法會(huì)讓這樣的頻繁使用的頁(yè)面常駐于內(nèi)存
,所以也就不會(huì)出現(xiàn)內(nèi)存換頁(yè)問題了。
11、出錯(cuò)信息的處理
—————————
你會(huì)處理出錯(cuò)信息嗎?哦,它并不是簡(jiǎn)單的輸出。看下面的示例:
?
??? if ( p == NULL ){
??????? printf ( "ERR: The pointer is NULL\n" );
??? }
?
告別學(xué)生時(shí)代的編程吧。這種編程很不利于維護(hù)和管理,出錯(cuò)信息或是提示信息,應(yīng)該統(tǒng)
一處理,而不是像上面這樣,寫成一個(gè)“硬編碼”。第10條對(duì)這方面的處理做了一部分說
明。如果要管理錯(cuò)誤信息,那就要有以下的處理:
?
??? /* 聲明出錯(cuò)代碼 */
??? #define???? ERR_NO_ERROR??? 0? /* No error???????????????? */
??? #define???? ERR_OPEN_FILE?? 1? /* Open file error????????? */
??? #define???? ERR_SEND_MESG?? 2? /* sending a message error? */
??????? /* 7 */?????? "A socket operation has failed",
??????? /* 8 */?????? "Permission denied",
??????? /* 9 */?????? "Bad configuration file format",
??????? /* 10 */????? "Communication time out",
??????? ......
??????? ......
??? };
?
請(qǐng)不要把這個(gè)東西放在頭文件中,因?yàn)槿绻愕倪@個(gè)頭文件被5個(gè)函數(shù)庫(kù)(.lib或是.a)所
用到,于是他就被鏈接在這5個(gè).lib或.a中,而如果你的一個(gè)程序用到了這5個(gè)函數(shù)庫(kù)中的
函數(shù),并且這些函數(shù)都用到了這個(gè)出錯(cuò)信息數(shù)組。那么這份信息將有5個(gè)副本存在于你的執(zhí)
行文件中。如果你的這個(gè)errmsg很大的話,而且你用到的函數(shù)庫(kù)更多的話,你的執(zhí)行文件
也會(huì)變得很大。
?
正確的寫法應(yīng)該把它寫到C文件中,然后在各個(gè)需要用到errmsg的C文件頭上加上 extern
char* errmsg[]; 的外部聲明,讓編譯器在鏈接時(shí)才去管他,這樣一來,就只會(huì)有一個(gè)err
msg存在于執(zhí)行文件中,而且,這樣做很利于封裝。
?
我曾遇到過的最瘋狂的事,就是在我的目標(biāo)文件中,這個(gè)errmsg一共有112個(gè)副本,執(zhí)行文
件有8M左右。當(dāng)我把errmsg放到C文件中,并為一千多個(gè)C文件加上了extern的聲明后,所
有的函數(shù)庫(kù)文件尺寸都下降了20%左右,而我的執(zhí)行文件只有5M了。一下子少了3M啊。
?
[ 備注 ]
—————
有朋友對(duì)我說,這個(gè)只是一個(gè)特例,因?yàn)椋绻鹐rrmsg在執(zhí)行文件中存在多個(gè)副本時(shí),可
以加快程序運(yùn)行速度,理由是errmsg的多個(gè)復(fù)本會(huì)讓系統(tǒng)的內(nèi)存換頁(yè)降低,達(dá)到效率提升
。像我們這里所說的errmsg只有一份,當(dāng)某函數(shù)要用errmsg時(shí),如果內(nèi)存隔得比較遠(yuǎn),會(huì)
產(chǎn)生換頁(yè),反而效率不高。
?
?
生副本導(dǎo)致執(zhí)行文件尺寸變大,不僅增加了系統(tǒng)裝載時(shí)間,也會(huì)讓一個(gè)程序在內(nèi)存中占更
多的頁(yè)面。而對(duì)于errmsg這樣數(shù)據(jù),一般來說,在系統(tǒng)運(yùn)行時(shí)不會(huì)經(jīng)常用到,所以還是產(chǎn)
生的內(nèi)存換頁(yè)也就不算頻繁。權(quán)衡之下,還是只有一份errmsg的效率高。即便是像logmsg
這樣頻繁使用的的數(shù)據(jù),操作系統(tǒng)的內(nèi)存調(diào)度算法會(huì)讓這樣的頻繁使用的頁(yè)面常駐于內(nèi)存
,所以也就不會(huì)出現(xiàn)內(nèi)存換頁(yè)問題了。
11、出錯(cuò)信息的處理
—————————
你會(huì)處理出錯(cuò)信息嗎?哦,它并不是簡(jiǎn)單的輸出。看下面的示例:
?
??? if ( p == NULL ){
??????? printf ( "ERR: The pointer is NULL\n" );
??? }
?
告別學(xué)生時(shí)代的編程吧。這種編程很不利于維護(hù)和管理,出錯(cuò)信息或是提示信息,應(yīng)該統(tǒng)
一處理,而不是像上面這樣,寫成一個(gè)“硬編碼”。第10條對(duì)這方面的處理做了一部分說
明。如果要管理錯(cuò)誤信息,那就要有以下的處理:
?
??? /* 聲明出錯(cuò)代碼 */
??? #define???? ERR_NO_ERROR??? 0? /* No error???????????????? */
??? #define???? ERR_OPEN_FILE?? 1? /* Open file error????????? */
??? #define???? ERR_SEND_MESG?? 2? /* sending a message error? */
??? #define???? ERR_BAD_ARGS??? 3? /* Bad arguments??????????? */
??? #define???? ERR_MEM_NONE??? 4? /* Memeroy is not enough??? */
??? #define???? ERR_SERV_DOWN?? 5? /* Service down try later?? */
??? #define???? ERR_UNKNOW_INFO 6? /* Unknow information?????? */
??? #define???? ERR_SOCKET_ERR? 7? /* Socket operation failed? */
??? #define???? ERR_PERMISSION? 8? /* Permission denied??????? */
??? #define???? ERR_BAD_FORMAT? 9? /* Bad configuration file?? */
??? #define???? ERR_TIME_OUT?? 10? /* Communication time out?? */
?
??? /* 聲明出錯(cuò)信息 */
??? char* errmsg[] = {
??????? /* 0 */?????? "No error",
??????? /* 1 */?????? "Open file error",
??????? /* 2 */?????? "Failed in sending/receiving a message",
??????? /* 3 */?????? "Bad arguments",
??????? /* 4 */?????? "Memeroy is not enough",
??????? /* 5 */?????? "Service is down; try later",
??????? /* 6 */?????? "Unknow information",
??????? /* 7 */?????? "A socket operation has failed",
??????? /* 8 */?????? "Permission denied",
??????? /* 9 */?????? "Bad configuration file format",
??????? /* 10 */????? "Communication time out",
??????? /* 10 */????? "Communication time out",
??? };
?
??? /* 聲明錯(cuò)誤代碼全局變量 */
??? long errno = 0;
?
??? /* 打印出錯(cuò)信息函數(shù) */
??? void perror( char* info)
??? {
??????? if ( info ){
??????????? printf("%s: %s\n", info, errmsg[errno] );
??????????? return;
??????? }
?
??????? printf("Error: %s\n", errmsg[errno] );
??? }
?
這個(gè)基本上是ANSI的錯(cuò)誤處理實(shí)現(xiàn)細(xì)節(jié)了,于是當(dāng)你程序中有錯(cuò)誤時(shí)你就可以這樣處理:
?
??? bool CheckPermission( char* userName )
??? {
??????? if ( strcpy(userName, "root") != 0 ){
??????????? errno = ERR_PERMISSION_DENIED;
??? #define???? ERR_MEM_NONE??? 4? /* Memeroy is not enough??? */
??? #define???? ERR_SERV_DOWN?? 5? /* Service down try later?? */
??? #define???? ERR_UNKNOW_INFO 6? /* Unknow information?????? */
??? #define???? ERR_SOCKET_ERR? 7? /* Socket operation failed? */
??? #define???? ERR_PERMISSION? 8? /* Permission denied??????? */
??? #define???? ERR_BAD_FORMAT? 9? /* Bad configuration file?? */
??? #define???? ERR_TIME_OUT?? 10? /* Communication time out?? */
?
??? /* 聲明出錯(cuò)信息 */
??? char* errmsg[] = {
??????? /* 0 */?????? "No error",
??????? /* 1 */?????? "Open file error",
??????? /* 2 */?????? "Failed in sending/receiving a message",
??????? /* 3 */?????? "Bad arguments",
??????? /* 4 */?????? "Memeroy is not enough",
??????? /* 5 */?????? "Service is down; try later",
??????? /* 6 */?????? "Unknow information",
??????? /* 7 */?????? "A socket operation has failed",
??????? /* 8 */?????? "Permission denied",
??????? /* 9 */?????? "Bad configuration file format",
??????? /* 10 */????? "Communication time out",
??????? /* 10 */????? "Communication time out",
??? };
?
??? /* 聲明錯(cuò)誤代碼全局變量 */
??? long errno = 0;
?
??? /* 打印出錯(cuò)信息函數(shù) */
??? void perror( char* info)
??? {
??????? if ( info ){
??????????? printf("%s: %s\n", info, errmsg[errno] );
??????????? return;
??????? }
?
??????? printf("Error: %s\n", errmsg[errno] );
??? }
?
這個(gè)基本上是ANSI的錯(cuò)誤處理實(shí)現(xiàn)細(xì)節(jié)了,于是當(dāng)你程序中有錯(cuò)誤時(shí)你就可以這樣處理:
?
??? bool CheckPermission( char* userName )
??? {
??????? if ( strcpy(userName, "root") != 0 ){
??????????? errno = ERR_PERMISSION_DENIED;
??????????? return (FALSE);
??????? }
?
??????? ...
??? }
?
??? main()
??? {
??????? ...
??????? if (! CheckPermission( username ) ){
??????????? perror("main()");
??????? }
??????? ...
??? }
?
一個(gè)即有共性,也有個(gè)性的錯(cuò)誤信息處理,這樣做有利同種錯(cuò)誤出一樣的信息,統(tǒng)一用戶
界面,而不會(huì)因?yàn)槲募蜷_失敗,A程序員出一個(gè)信息,B程序員又出一個(gè)信息。而且這樣
做,非常容易維護(hù)。代碼也易讀。
?
當(dāng)然,物極必反,也沒有必要把所有的輸出都放到errmsg中,抽取比較重要的出錯(cuò)信息或
是提示信息是其關(guān)鍵,但即使這樣,這也包括了大多數(shù)的信息。
?
?
?
?
?
12、常用函數(shù)和循環(huán)語(yǔ)句中的被計(jì)算量
—————————————————
看一下下面這個(gè)例子:
?
??? for( i=0; i<1000; i++ ){
??????? GetLocalHostName( hostname );
??????? ...
??? }
?
GetLocalHostName的意思是取得當(dāng)前計(jì)算機(jī)名,在循環(huán)體中,它會(huì)被調(diào)用1000次啊。這是
多么的沒有效率的事啊。應(yīng)該把這個(gè)函數(shù)拿到循環(huán)體外,這樣只調(diào)用一次,效率得到了很
大的提高。雖然,我們的編譯器會(huì)進(jìn)行優(yōu)化,會(huì)把循環(huán)體內(nèi)的不變的東西拿到循環(huán)外面,
但是,你相信所有編譯器會(huì)知道哪些是不變的嗎?我覺得編譯器不可靠。最好還是自己動(dòng)
手吧。
?
同樣,對(duì)于常用函數(shù)中的不變量,如:
?
GetLocalHostName(char* name)
{
{
??? char funcName[] = "GetLocalHostName";
?
??? sys_log( "%s begin......", funcName );
??? ...
??? sys_log( "%s end......", funcName );
}
?
如果這是一個(gè)經(jīng)常調(diào)用的函數(shù),每次調(diào)用時(shí)都要對(duì)funcName進(jìn)行分配內(nèi)存,這個(gè)開銷很大
啊。把這個(gè)變量聲明成static吧,當(dāng)函數(shù)再次被調(diào)用時(shí),就會(huì)省去了分配內(nèi)存的開銷,執(zhí)
行效率也很好。
?
?
?
?
13、函數(shù)名和變量名的命名
————————————
我看到許多程序?qū)ψ兞棵秃瘮?shù)名的取名很草率,特別是變量名,什么a,b,c,aa,bb,cc,
還有什么flag1,flag2, cnt1, cnt2,這同樣是一種沒有“修養(yǎng)”的行為。即便加上好的注
釋。好的變量名或是函數(shù)名,我認(rèn)為應(yīng)該有以下的規(guī)則:
?
??? 1) 直觀并且可以拼讀,可望文知意,不必“解碼”。
??? 2) 名字的長(zhǎng)度應(yīng)該即要最短的長(zhǎng)度,也要能最大限度的表達(dá)其含義。
??????? }
?
??????? ...
??? }
?
??? main()
??? {
??????? ...
??????? if (! CheckPermission( username ) ){
??????????? perror("main()");
??????? }
??????? ...
??? }
?
一個(gè)即有共性,也有個(gè)性的錯(cuò)誤信息處理,這樣做有利同種錯(cuò)誤出一樣的信息,統(tǒng)一用戶
界面,而不會(huì)因?yàn)槲募蜷_失敗,A程序員出一個(gè)信息,B程序員又出一個(gè)信息。而且這樣
做,非常容易維護(hù)。代碼也易讀。
?
當(dāng)然,物極必反,也沒有必要把所有的輸出都放到errmsg中,抽取比較重要的出錯(cuò)信息或
是提示信息是其關(guān)鍵,但即使這樣,這也包括了大多數(shù)的信息。
?
?
?
?
?
12、常用函數(shù)和循環(huán)語(yǔ)句中的被計(jì)算量
—————————————————
看一下下面這個(gè)例子:
?
??? for( i=0; i<1000; i++ ){
??????? GetLocalHostName( hostname );
??????? ...
??? }
?
GetLocalHostName的意思是取得當(dāng)前計(jì)算機(jī)名,在循環(huán)體中,它會(huì)被調(diào)用1000次啊。這是
多么的沒有效率的事啊。應(yīng)該把這個(gè)函數(shù)拿到循環(huán)體外,這樣只調(diào)用一次,效率得到了很
大的提高。雖然,我們的編譯器會(huì)進(jìn)行優(yōu)化,會(huì)把循環(huán)體內(nèi)的不變的東西拿到循環(huán)外面,
但是,你相信所有編譯器會(huì)知道哪些是不變的嗎?我覺得編譯器不可靠。最好還是自己動(dòng)
手吧。
?
同樣,對(duì)于常用函數(shù)中的不變量,如:
?
GetLocalHostName(char* name)
{
{
??? char funcName[] = "GetLocalHostName";
?
??? sys_log( "%s begin......", funcName );
??? ...
??? sys_log( "%s end......", funcName );
}
?
如果這是一個(gè)經(jīng)常調(diào)用的函數(shù),每次調(diào)用時(shí)都要對(duì)funcName進(jìn)行分配內(nèi)存,這個(gè)開銷很大
啊。把這個(gè)變量聲明成static吧,當(dāng)函數(shù)再次被調(diào)用時(shí),就會(huì)省去了分配內(nèi)存的開銷,執(zhí)
行效率也很好。
?
?
?
?
13、函數(shù)名和變量名的命名
————————————
我看到許多程序?qū)ψ兞棵秃瘮?shù)名的取名很草率,特別是變量名,什么a,b,c,aa,bb,cc,
還有什么flag1,flag2, cnt1, cnt2,這同樣是一種沒有“修養(yǎng)”的行為。即便加上好的注
釋。好的變量名或是函數(shù)名,我認(rèn)為應(yīng)該有以下的規(guī)則:
?
??? 1) 直觀并且可以拼讀,可望文知意,不必“解碼”。
??? 2) 名字的長(zhǎng)度應(yīng)該即要最短的長(zhǎng)度,也要能最大限度的表達(dá)其含義。
??? 3) 不要全部大寫,也不要全部小寫,應(yīng)該大小寫都有,如:GetLocalHostName 或是
UserAccount。
??? 4) 可以簡(jiǎn)寫,但簡(jiǎn)寫得要讓人明白,如:ErrorCode -> ErrCode,
ServerListener -> ServLisner,UserAccount -> UsrAcct 等。
??? 5) 為了避免全局函數(shù)和變量名字沖突,可以加上一些前綴,一般以模塊簡(jiǎn)稱做為前綴
。
??? 6) 全局變量統(tǒng)一加一個(gè)前綴或是后綴,讓人一看到這個(gè)變量就知道是全局的。
??? 7) 用匈牙利命名法命名函數(shù)參數(shù),局部變量。但還是要堅(jiān)持“望文生意”的原則。
??? 8) 與標(biāo)準(zhǔn)庫(kù)(如:STL)或開發(fā)庫(kù)(如:MFC)的命名風(fēng)格保持一致。
?
?
?
?
14、函數(shù)的傳值和傳指針
————————————
向函數(shù)傳參數(shù)時(shí),一般而言,傳入非const的指針時(shí),就表示,在函數(shù)中要修改這個(gè)指針把
指內(nèi)存中的數(shù)據(jù)。如果是傳值,那么無論在函數(shù)內(nèi)部怎么修改這個(gè)值,也影響不到傳過來
的值,因?yàn)閭髦凳侵粌?nèi)存拷貝。
?
什么?你說這個(gè)特性你明白了,好吧,讓我們看看下面的這個(gè)例程:
?
void
void
GetVersion(char* pStr)
{
??? pStr = malloc(10);
??? strcpy ( pStr, "2.0" );
}
?
main()
{
??? char* ver = NULL;
??? GetVersion ( ver );
??? ...
??? ...
??? free ( ver );
}
?
我保證,類似這樣的問題是一個(gè)新手最容易犯的錯(cuò)誤。程序中妄圖通過函數(shù)GetVersion給
指針ver分配空間,但這種方法根本沒有什么作用,原因就是——這是傳值,不是傳指針。
你或許會(huì)和我爭(zhēng)論,我分明傳的時(shí)指針啊?再仔細(xì)看看,其實(shí),你傳的是指針其實(shí)是在傳
值。
?
?
?
?
15、修改別人程序的修養(yǎng)
———————————
?
當(dāng)你維護(hù)別人的程序時(shí),請(qǐng)不要非常主觀臆斷的把已有的程序刪除或是修改。我經(jīng)常看到
有的程序員直接在別人的程序上修改表達(dá)式或是語(yǔ)句。修改別人的程序時(shí),請(qǐng)不要?jiǎng)h除別
人的程序,如果你覺得別人的程序有所不妥,請(qǐng)注釋掉,然后添加自己的處理程序,必竟
,你不可能100%的知道別人的意圖,所以為了可以恢復(fù),請(qǐng)不依賴于CVS或是SourceSafe這
種版本控制軟件,還是要在源碼上給別人看到你修改程序的意圖和步驟。這是程序維護(hù)時(shí)
,一個(gè)有修養(yǎng)的程序員所應(yīng)該做的。
?
如下所示,這就是一種比較好的修改方法:
?
??? /*
???? * ----- commented by haoel 2003/04/12 ------
???? *
???? *?? char* p = ( char* ) malloc( 10 );
???? *?? memset( p, 0, 10 );
???? */
?
??? /* ------ Added by haoel?? 2003/04/12 ----- */
???? char* p = ( char* )calloc( 10, sizeof char );
??? /* ---------------------------------------- */
???? *?? char* p = 開始使勁) malloc( 10 );
???? *?? memset( p, 0, 10 );
???? */
?
??? /* ------ Added by haoel?? 2003/04/12 ----- */
???? char* p = ( char* )calloc( 10, sizeof char );
??? /* ---------------------------------------- */
??? ...
?
當(dāng)然,這種方法是在軟件維護(hù)時(shí)使用的,這樣的方法,可以讓再維護(hù)的人很容易知道以前
的代碼更改的動(dòng)作和意圖,而且這也是對(duì)原作者的一種尊敬。
?
以“注釋 — 添加”方式修改別人的程序,要好于直接刪除別人的程序。
--
16、把相同或近乎相同的代碼形成函數(shù)和宏
—————————————————————
?
有人說,最好的程序員,就是最喜歡“偷懶”的程序,其中不無道理。
?
如果你有一些程序的代碼片段很相似,或直接就是一樣的,請(qǐng)把他們放在一個(gè)函數(shù)中。而
如果這段代碼不多,而且會(huì)被經(jīng)常使用,你還想避免函數(shù)調(diào)用的開銷,那么就把他寫成宏
吧。
?
千萬不要讓同一份代碼或是功能相似的代碼在多個(gè)地方存在,不然如果功能一變,你就要
修改好幾處地方,這種會(huì)給維護(hù)帶來巨大的麻煩,所以,做到“一改百改”,還是要形成
函數(shù)或是宏。
?
?
?
17、表達(dá)式中的括號(hào)
17、表達(dá)式中的括號(hào)
—————————
?
如果一個(gè)比較復(fù)雜的表達(dá)式中,你并不是很清楚各個(gè)操作符的憂先級(jí),即使是你很清楚優(yōu)
先級(jí),也請(qǐng)加上括號(hào),不然,別人或是自己下一次讀程序時(shí),一不小心就看走眼理解錯(cuò)了
,為了避免這種“誤解”,還有讓自己的程序更為清淅,還是加上括號(hào)吧。
?
比如,對(duì)一個(gè)結(jié)構(gòu)的成員取地址:
?
??? GetUserAge( &( UserInfo->age ) );
?
雖然,&UserInfo->age中,->操作符的優(yōu)先級(jí)最高,但加上一個(gè)括號(hào),會(huì)讓人一眼就看明
白你的代碼是什么意思。
?
再比如,一個(gè)很長(zhǎng)的條件判斷:
?
if ( ( ch[0] >= '0' || ch[0] <= '9' ) &&
???? ( ch[1] >= 'a' || ch[1] <= 'z' ) &&
???? ( ch[2] >= 'A' || ch[2] <= 'Z' )??? )
?
括號(hào),再加上空格和換行,你的代碼是不是很容易讀懂了?
?
?
?
18、函數(shù)參數(shù)中的const
———————————
?
對(duì)于一些函數(shù)中的指針參數(shù),如果在函數(shù)中只讀,請(qǐng)將其用const修飾,這樣,別人一讀到
你的函數(shù)接口時(shí),就會(huì)知道你的意圖是這個(gè)參數(shù)是[in],如果沒有const時(shí),參數(shù)表示[in/
out],注意函數(shù)接口中的const使用,利于程序的維護(hù)和避免犯一些錯(cuò)誤。
?
雖然,const修飾的指針,如:const char* p,在C中一點(diǎn)用也沒有,因?yàn)椴还苣愕穆暶魇?
不是const,指針的內(nèi)容照樣能改,因?yàn)榫幾g器會(huì)強(qiáng)制轉(zhuǎn)換,但是加上這樣一個(gè)說明,有利
于程序的閱讀和編譯。因?yàn)樵贑中,修改一個(gè)const指針?biāo)赶虻膬?nèi)存時(shí),會(huì)報(bào)一個(gè)Warning
。這會(huì)引起程序員的注意。
?
C++中對(duì)const定義的就很嚴(yán)格了,所以C++中要多多的使用const,const的成員函數(shù),cons
t的變量,這樣會(huì)對(duì)讓你的代碼和你的程序更加完整和易讀。(關(guān)于C++的const我就不多說
了)
?
?
?
19、函數(shù)的參數(shù)個(gè)數(shù)(多了請(qǐng)用結(jié)構(gòu))
—————————————————
?
函數(shù)的參數(shù)個(gè)數(shù)最好不要太多,一般來說6個(gè)左右就可以了,眾多的函數(shù)參數(shù)會(huì)讓讀代碼的
UserAccount。
??? 4) 可以簡(jiǎn)寫,但簡(jiǎn)寫得要讓人明白,如:ErrorCode -> ErrCode,
ServerListener -> ServLisner,UserAccount -> UsrAcct 等。
??? 5) 為了避免全局函數(shù)和變量名字沖突,可以加上一些前綴,一般以模塊簡(jiǎn)稱做為前綴
。
??? 6) 全局變量統(tǒng)一加一個(gè)前綴或是后綴,讓人一看到這個(gè)變量就知道是全局的。
??? 7) 用匈牙利命名法命名函數(shù)參數(shù),局部變量。但還是要堅(jiān)持“望文生意”的原則。
??? 8) 與標(biāo)準(zhǔn)庫(kù)(如:STL)或開發(fā)庫(kù)(如:MFC)的命名風(fēng)格保持一致。
?
?
?
?
14、函數(shù)的傳值和傳指針
————————————
向函數(shù)傳參數(shù)時(shí),一般而言,傳入非const的指針時(shí),就表示,在函數(shù)中要修改這個(gè)指針把
指內(nèi)存中的數(shù)據(jù)。如果是傳值,那么無論在函數(shù)內(nèi)部怎么修改這個(gè)值,也影響不到傳過來
的值,因?yàn)閭髦凳侵粌?nèi)存拷貝。
?
什么?你說這個(gè)特性你明白了,好吧,讓我們看看下面的這個(gè)例程:
?
void
void
GetVersion(char* pStr)
{
??? pStr = malloc(10);
??? strcpy ( pStr, "2.0" );
}
?
main()
{
??? char* ver = NULL;
??? GetVersion ( ver );
??? ...
??? ...
??? free ( ver );
}
?
我保證,類似這樣的問題是一個(gè)新手最容易犯的錯(cuò)誤。程序中妄圖通過函數(shù)GetVersion給
指針ver分配空間,但這種方法根本沒有什么作用,原因就是——這是傳值,不是傳指針。
你或許會(huì)和我爭(zhēng)論,我分明傳的時(shí)指針啊?再仔細(xì)看看,其實(shí),你傳的是指針其實(shí)是在傳
值。
?
?
?
?
15、修改別人程序的修養(yǎng)
———————————
?
當(dāng)你維護(hù)別人的程序時(shí),請(qǐng)不要非常主觀臆斷的把已有的程序刪除或是修改。我經(jīng)常看到
有的程序員直接在別人的程序上修改表達(dá)式或是語(yǔ)句。修改別人的程序時(shí),請(qǐng)不要?jiǎng)h除別
人的程序,如果你覺得別人的程序有所不妥,請(qǐng)注釋掉,然后添加自己的處理程序,必竟
,你不可能100%的知道別人的意圖,所以為了可以恢復(fù),請(qǐng)不依賴于CVS或是SourceSafe這
種版本控制軟件,還是要在源碼上給別人看到你修改程序的意圖和步驟。這是程序維護(hù)時(shí)
,一個(gè)有修養(yǎng)的程序員所應(yīng)該做的。
?
如下所示,這就是一種比較好的修改方法:
?
??? /*
???? * ----- commented by haoel 2003/04/12 ------
???? *
???? *?? char* p = ( char* ) malloc( 10 );
???? *?? memset( p, 0, 10 );
???? */
?
??? /* ------ Added by haoel?? 2003/04/12 ----- */
???? char* p = ( char* )calloc( 10, sizeof char );
??? /* ---------------------------------------- */
???? *?? char* p = 開始使勁) malloc( 10 );
???? *?? memset( p, 0, 10 );
???? */
?
??? /* ------ Added by haoel?? 2003/04/12 ----- */
???? char* p = ( char* )calloc( 10, sizeof char );
??? /* ---------------------------------------- */
??? ...
?
當(dāng)然,這種方法是在軟件維護(hù)時(shí)使用的,這樣的方法,可以讓再維護(hù)的人很容易知道以前
的代碼更改的動(dòng)作和意圖,而且這也是對(duì)原作者的一種尊敬。
?
以“注釋 — 添加”方式修改別人的程序,要好于直接刪除別人的程序。
--
16、把相同或近乎相同的代碼形成函數(shù)和宏
—————————————————————
?
有人說,最好的程序員,就是最喜歡“偷懶”的程序,其中不無道理。
?
如果你有一些程序的代碼片段很相似,或直接就是一樣的,請(qǐng)把他們放在一個(gè)函數(shù)中。而
如果這段代碼不多,而且會(huì)被經(jīng)常使用,你還想避免函數(shù)調(diào)用的開銷,那么就把他寫成宏
吧。
?
千萬不要讓同一份代碼或是功能相似的代碼在多個(gè)地方存在,不然如果功能一變,你就要
修改好幾處地方,這種會(huì)給維護(hù)帶來巨大的麻煩,所以,做到“一改百改”,還是要形成
函數(shù)或是宏。
?
?
?
17、表達(dá)式中的括號(hào)
17、表達(dá)式中的括號(hào)
—————————
?
如果一個(gè)比較復(fù)雜的表達(dá)式中,你并不是很清楚各個(gè)操作符的憂先級(jí),即使是你很清楚優(yōu)
先級(jí),也請(qǐng)加上括號(hào),不然,別人或是自己下一次讀程序時(shí),一不小心就看走眼理解錯(cuò)了
,為了避免這種“誤解”,還有讓自己的程序更為清淅,還是加上括號(hào)吧。
?
比如,對(duì)一個(gè)結(jié)構(gòu)的成員取地址:
?
??? GetUserAge( &( UserInfo->age ) );
?
雖然,&UserInfo->age中,->操作符的優(yōu)先級(jí)最高,但加上一個(gè)括號(hào),會(huì)讓人一眼就看明
白你的代碼是什么意思。
?
再比如,一個(gè)很長(zhǎng)的條件判斷:
?
if ( ( ch[0] >= '0' || ch[0] <= '9' ) &&
???? ( ch[1] >= 'a' || ch[1] <= 'z' ) &&
???? ( ch[2] >= 'A' || ch[2] <= 'Z' )??? )
?
括號(hào),再加上空格和換行,你的代碼是不是很容易讀懂了?
?
?
?
18、函數(shù)參數(shù)中的const
———————————
?
對(duì)于一些函數(shù)中的指針參數(shù),如果在函數(shù)中只讀,請(qǐng)將其用const修飾,這樣,別人一讀到
你的函數(shù)接口時(shí),就會(huì)知道你的意圖是這個(gè)參數(shù)是[in],如果沒有const時(shí),參數(shù)表示[in/
out],注意函數(shù)接口中的const使用,利于程序的維護(hù)和避免犯一些錯(cuò)誤。
?
雖然,const修飾的指針,如:const char* p,在C中一點(diǎn)用也沒有,因?yàn)椴还苣愕穆暶魇?
不是const,指針的內(nèi)容照樣能改,因?yàn)榫幾g器會(huì)強(qiáng)制轉(zhuǎn)換,但是加上這樣一個(gè)說明,有利
于程序的閱讀和編譯。因?yàn)樵贑中,修改一個(gè)const指針?biāo)赶虻膬?nèi)存時(shí),會(huì)報(bào)一個(gè)Warning
。這會(huì)引起程序員的注意。
?
C++中對(duì)const定義的就很嚴(yán)格了,所以C++中要多多的使用const,const的成員函數(shù),cons
t的變量,這樣會(huì)對(duì)讓你的代碼和你的程序更加完整和易讀。(關(guān)于C++的const我就不多說
了)
?
?
?
19、函數(shù)的參數(shù)個(gè)數(shù)(多了請(qǐng)用結(jié)構(gòu))
—————————————————
?
函數(shù)的參數(shù)個(gè)數(shù)最好不要太多,一般來說6個(gè)左右就可以了,眾多的函數(shù)參數(shù)會(huì)讓讀代碼的
人一眼看上去就很頭昏,而且也不利于維護(hù)。如果參數(shù)眾多,還請(qǐng)使用結(jié)構(gòu)來傳遞參數(shù)。
這樣做有利于數(shù)據(jù)的封裝和程序的簡(jiǎn)潔性。
?
也利于使用函數(shù)的人,因?yàn)槿绻愕暮瘮?shù)個(gè)數(shù)很多,比如12個(gè),調(diào)用者很容易搞錯(cuò)參數(shù)的
順序和個(gè)數(shù),而使用結(jié)構(gòu)struct來傳遞參數(shù),就可以不管參數(shù)的順序。
?
而且,函數(shù)很容易被修改,如果需要給函數(shù)增加參數(shù),不需要更改函數(shù)接口,只需更改結(jié)
構(gòu)體和函數(shù)內(nèi)部處理,而對(duì)于調(diào)用函數(shù)的程序來說,這個(gè)動(dòng)作是透明的。
?
?
?
?
20、函數(shù)的返回類型,不要省略
——————————————
?
我看到很多程序?qū)懞瘮?shù)時(shí),在函數(shù)的返回類型方面不太注意。如果一個(gè)函數(shù)沒有返回值,
也請(qǐng)?jiān)诤瘮?shù)前面加上void的修飾。而有的程序員偷懶,在返回int的函數(shù)則什么不修飾(因
為如果不修飾,則默認(rèn)返回int),這種習(xí)慣很不好,還是為了原代碼的易讀性,加上int
吧。
?
所以函數(shù)的返回值類型,請(qǐng)不要省略。
?
?
另外,對(duì)于void的函數(shù),我們往往會(huì)忘了return,由于某些C/C++的編譯器比較敏感,會(huì)報(bào)
一些警告,所以即使是void的函數(shù),我們?cè)趦?nèi)部最好也要加上return的語(yǔ)句,這有助于代
碼的編譯。
?
?
?
?
21、goto語(yǔ)句的使用
—————————
?
N年前,軟件開發(fā)的一代宗師——迪杰斯特拉(Dijkstra)說過:“goto statment is
harmful !!”,并建議取消goto語(yǔ)句。因?yàn)間oto語(yǔ)句不利于程序代碼的維護(hù)性。
?
這里我也強(qiáng)烈建議不要使用goto語(yǔ)句,除非下面的這種情況:
?
?
??? #define FREE(p) if(p) { \
??????????????????????? free(p); \
??????????????????????? p = NULL; \
??????????????????? }
?
??? main()
??? main()
??? {
??????? char *fname=NULL, *lname=NULL, *mname=NULL;
?
??????? fname = ( char* ) calloc ( 20, sizeof(char) );
??????? if ( fname == NULL ){
goto ErrHandle;
??????? }
?
??????? lname = ( char* ) calloc ( 20, sizeof(char) );
??????? if ( lname == NULL ){
??????????? goto ErrHandle;
??????? }
?
??????? mname = ( char* ) calloc ( 20, sizeof(char) );
??????? if ( mname == NULL ){
??????????? goto ErrHandle;
??????? }
?
??????? ......
?
?
???? ErrHandle:
???? ErrHandle:
??????? FREE(fname);
??????? FREE(lname);
??????? FREE(mname);
??????? ReportError(ERR_NO_MEMOEY);
???? }
?
也只有在這種情況下,goto語(yǔ)句會(huì)讓你的程序更易讀,更容易維護(hù)。(在用嵌C來對(duì)數(shù)據(jù)庫(kù)
設(shè)置游標(biāo)操作時(shí),或是對(duì)數(shù)據(jù)庫(kù)建立鏈接時(shí),也會(huì)遇到這種結(jié)構(gòu))
?
?
?
?
22、宏的使用
——————
?
很多程序員不知道C中的“宏”到底是什么意思?特別是當(dāng)宏有參數(shù)的時(shí)候,經(jīng)常把宏和函
數(shù)混淆。我想在這里我還是先講講“宏”,宏只是一種定義,他定義了一個(gè)語(yǔ)句塊,當(dāng)程
序編譯時(shí),編譯器首先要執(zhí)行一個(gè)“替換”源程序的動(dòng)作,把宏引用的地方替換成宏定義
的語(yǔ)句塊,就像文本文件替換一樣。這個(gè)動(dòng)作術(shù)語(yǔ)叫“宏的展開”
?
使用宏是比較“危險(xiǎn)”的,因?yàn)槟悴恢篮暾归_后會(huì)是什么一個(gè)樣子。例如下面這個(gè)宏:
?
?
??? #define? MAX(a, b)???? a>b?a:b
?
當(dāng)我們這樣使用宏時(shí),沒有什么問題: MAX( num1, num2 ); 因?yàn)楹暾归_后變成
num1>num2?num1:num2;。 但是,如果是這樣調(diào)用的,MAX( 17+32, 25+21 ); 呢,編譯時(shí)
出現(xiàn)錯(cuò)誤,原因是,宏展開后變成:17+32>25+21?17+32:25+21,哇,這是什么啊?
?
所以,宏在使用時(shí),參數(shù)一定要加上括號(hào),上述的那個(gè)例子改成如下所示就能解決問題了
。
?
??? #define? MAX( (a), (b) )???? (a)>(b)?(a):(b)
?
即使是這樣,也不這個(gè)宏也還是有Bug,因?yàn)槿绻疫@樣調(diào)用 MAX(i++, j++); , 經(jīng)過這
個(gè)宏以后,i和j都被累加了兩次,這絕不是我們想要的。
?
所以,在宏的使用上還是要謹(jǐn)慎考慮,因?yàn)楹暾归_是的結(jié)果是很難讓人預(yù)料的。而且雖然
,宏的執(zhí)行很快(因?yàn)闆]有函數(shù)調(diào)用的開銷),但宏會(huì)讓源代碼澎漲,使目標(biāo)文件尺寸變
大,(如:一個(gè)50行的宏,程序中有1000個(gè)地方用到,宏展開后會(huì)很不得了),相反不能
讓程序執(zhí)行得更快(因?yàn)閳?zhí)行文件變大,運(yùn)行時(shí)系統(tǒng)換頁(yè)頻繁)。
?
因此,在決定是用函數(shù),還是用宏時(shí)得要小心。
--
?
。???????????????? 開始使勁
?
??? #define? MAX( (a), (b) )???? (a)>(b)?(a):(b)
?
即使是這樣,也不這個(gè)宏也還是有Bug,因?yàn)槿绻疫@樣調(diào)用 MAX(i++, j++); , 經(jīng)過這
個(gè)宏以后,i和j都被累加了兩次,這絕不是我們想要的。
?
所以,在宏的使用上還是要謹(jǐn)慎考慮,因?yàn)楹暾归_是的結(jié)果是很難讓人預(yù)料的。而且雖然
,宏的執(zhí)行很快(因?yàn)闆]有函數(shù)調(diào)用的開銷),但宏會(huì)讓源代碼澎漲,使目標(biāo)文件尺寸變
大,(如:一個(gè)50行的宏,程序中有1000個(gè)地方用到,宏展開后會(huì)很不得了),相反不能
讓程序執(zhí)行得更快(因?yàn)閳?zhí)行文件變大,運(yùn)行時(shí)系統(tǒng)換頁(yè)頻繁)。
?
因此,在決定是用函數(shù),還是用宏時(shí)得要小心。
--
23、static的使用
————————
static關(guān)鍵字,表示了“靜態(tài)”,一般來說,他會(huì)被經(jīng)常用于變量和函數(shù)。一個(gè)static的
變量,其實(shí)就是全局變量,只不過他是有作用域的全局變量。比如一個(gè)函數(shù)中的static變
量:
?
char*
getConsumerName()
{
??? static int cnt = 0;
?
??? ....
??? cnt++;
??? ....
}
?
?
cnt變量的值會(huì)跟隨著函數(shù)的調(diào)用次而遞增,函數(shù)退出后,cnt的值還存在,只是cnt只能在
函數(shù)中才能被訪問。而cnt的內(nèi)存也只會(huì)在函數(shù)第一次被調(diào)用時(shí)才會(huì)被分配和初始化,以后
每次進(jìn)入函數(shù),都不為static分配了,而直接使用上一次的值。
?
對(duì)于一些被經(jīng)常調(diào)用的函數(shù)內(nèi)的常量,最好也聲明成static(參見第12條)
?
但static的最多的用處卻不在這里,其最大的作用的控制訪問,在C中如果一個(gè)函數(shù)或是一
個(gè)全局變量被聲明為static,那么,這個(gè)函數(shù)和這個(gè)全局變量,將只能在這個(gè)C文件中被訪
問,如果別的C文件中調(diào)用這個(gè)C文件中的函數(shù),或是使用其中的全局(用extern關(guān)鍵字)
,將會(huì)發(fā)生鏈接時(shí)錯(cuò)誤。這個(gè)特性可以用于數(shù)據(jù)和程序保密。
?
?
?
24、函數(shù)中的代碼尺寸
——————————
一個(gè)函數(shù)完成一個(gè)具體的功能,一般來說,一個(gè)函數(shù)中的代碼最好不要超過600行左右,越
少越好,最好的函數(shù)一般在100行以內(nèi),300行左右的孫函數(shù)就差不多了。有證據(jù)表明,一
個(gè)函數(shù)中的代碼如果超過500行,就會(huì)有和別的函數(shù)相同或是相近的代碼,也就是說,就可
以再寫另一個(gè)函數(shù)。
?
另外,函數(shù)一般是完成一個(gè)特定的功能,千萬忌諱在一個(gè)函數(shù)中做許多件不同的事。函數(shù)
的功能越單一越好,一方面有利于函數(shù)的易讀性,另一方面更有利于代碼的維護(hù)和重用,
這樣做有利于數(shù)據(jù)的封裝和程序的簡(jiǎn)潔性。
?
也利于使用函數(shù)的人,因?yàn)槿绻愕暮瘮?shù)個(gè)數(shù)很多,比如12個(gè),調(diào)用者很容易搞錯(cuò)參數(shù)的
順序和個(gè)數(shù),而使用結(jié)構(gòu)struct來傳遞參數(shù),就可以不管參數(shù)的順序。
?
而且,函數(shù)很容易被修改,如果需要給函數(shù)增加參數(shù),不需要更改函數(shù)接口,只需更改結(jié)
構(gòu)體和函數(shù)內(nèi)部處理,而對(duì)于調(diào)用函數(shù)的程序來說,這個(gè)動(dòng)作是透明的。
?
?
?
?
20、函數(shù)的返回類型,不要省略
——————————————
?
我看到很多程序?qū)懞瘮?shù)時(shí),在函數(shù)的返回類型方面不太注意。如果一個(gè)函數(shù)沒有返回值,
也請(qǐng)?jiān)诤瘮?shù)前面加上void的修飾。而有的程序員偷懶,在返回int的函數(shù)則什么不修飾(因
為如果不修飾,則默認(rèn)返回int),這種習(xí)慣很不好,還是為了原代碼的易讀性,加上int
吧。
?
所以函數(shù)的返回值類型,請(qǐng)不要省略。
?
?
另外,對(duì)于void的函數(shù),我們往往會(huì)忘了return,由于某些C/C++的編譯器比較敏感,會(huì)報(bào)
一些警告,所以即使是void的函數(shù),我們?cè)趦?nèi)部最好也要加上return的語(yǔ)句,這有助于代
碼的編譯。
?
?
?
?
21、goto語(yǔ)句的使用
—————————
?
N年前,軟件開發(fā)的一代宗師——迪杰斯特拉(Dijkstra)說過:“goto statment is
harmful !!”,并建議取消goto語(yǔ)句。因?yàn)間oto語(yǔ)句不利于程序代碼的維護(hù)性。
?
這里我也強(qiáng)烈建議不要使用goto語(yǔ)句,除非下面的這種情況:
?
?
??? #define FREE(p) if(p) { \
??????????????????????? free(p); \
??????????????????????? p = NULL; \
??????????????????? }
?
??? main()
??? main()
??? {
??????? char *fname=NULL, *lname=NULL, *mname=NULL;
?
??????? fname = ( char* ) calloc ( 20, sizeof(char) );
??????? if ( fname == NULL ){
goto ErrHandle;
??????? }
?
??????? lname = ( char* ) calloc ( 20, sizeof(char) );
??????? if ( lname == NULL ){
??????????? goto ErrHandle;
??????? }
?
??????? mname = ( char* ) calloc ( 20, sizeof(char) );
??????? if ( mname == NULL ){
??????????? goto ErrHandle;
??????? }
?
??????? ......
?
?
???? ErrHandle:
???? ErrHandle:
??????? FREE(fname);
??????? FREE(lname);
??????? FREE(mname);
??????? ReportError(ERR_NO_MEMOEY);
???? }
?
也只有在這種情況下,goto語(yǔ)句會(huì)讓你的程序更易讀,更容易維護(hù)。(在用嵌C來對(duì)數(shù)據(jù)庫(kù)
設(shè)置游標(biāo)操作時(shí),或是對(duì)數(shù)據(jù)庫(kù)建立鏈接時(shí),也會(huì)遇到這種結(jié)構(gòu))
?
?
?
?
22、宏的使用
——————
?
很多程序員不知道C中的“宏”到底是什么意思?特別是當(dāng)宏有參數(shù)的時(shí)候,經(jīng)常把宏和函
數(shù)混淆。我想在這里我還是先講講“宏”,宏只是一種定義,他定義了一個(gè)語(yǔ)句塊,當(dāng)程
序編譯時(shí),編譯器首先要執(zhí)行一個(gè)“替換”源程序的動(dòng)作,把宏引用的地方替換成宏定義
的語(yǔ)句塊,就像文本文件替換一樣。這個(gè)動(dòng)作術(shù)語(yǔ)叫“宏的展開”
?
使用宏是比較“危險(xiǎn)”的,因?yàn)槟悴恢篮暾归_后會(huì)是什么一個(gè)樣子。例如下面這個(gè)宏:
?
?
??? #define? MAX(a, b)???? a>b?a:b
?
當(dāng)我們這樣使用宏時(shí),沒有什么問題: MAX( num1, num2 ); 因?yàn)楹暾归_后變成
num1>num2?num1:num2;。 但是,如果是這樣調(diào)用的,MAX( 17+32, 25+21 ); 呢,編譯時(shí)
出現(xiàn)錯(cuò)誤,原因是,宏展開后變成:17+32>25+21?17+32:25+21,哇,這是什么啊?
?
所以,宏在使用時(shí),參數(shù)一定要加上括號(hào),上述的那個(gè)例子改成如下所示就能解決問題了
。
?
??? #define? MAX( (a), (b) )???? (a)>(b)?(a):(b)
?
即使是這樣,也不這個(gè)宏也還是有Bug,因?yàn)槿绻疫@樣調(diào)用 MAX(i++, j++); , 經(jīng)過這
個(gè)宏以后,i和j都被累加了兩次,這絕不是我們想要的。
?
所以,在宏的使用上還是要謹(jǐn)慎考慮,因?yàn)楹暾归_是的結(jié)果是很難讓人預(yù)料的。而且雖然
,宏的執(zhí)行很快(因?yàn)闆]有函數(shù)調(diào)用的開銷),但宏會(huì)讓源代碼澎漲,使目標(biāo)文件尺寸變
大,(如:一個(gè)50行的宏,程序中有1000個(gè)地方用到,宏展開后會(huì)很不得了),相反不能
讓程序執(zhí)行得更快(因?yàn)閳?zhí)行文件變大,運(yùn)行時(shí)系統(tǒng)換頁(yè)頻繁)。
?
因此,在決定是用函數(shù),還是用宏時(shí)得要小心。
--
?
。???????????????? 開始使勁
?
??? #define? MAX( (a), (b) )???? (a)>(b)?(a):(b)
?
即使是這樣,也不這個(gè)宏也還是有Bug,因?yàn)槿绻疫@樣調(diào)用 MAX(i++, j++); , 經(jīng)過這
個(gè)宏以后,i和j都被累加了兩次,這絕不是我們想要的。
?
所以,在宏的使用上還是要謹(jǐn)慎考慮,因?yàn)楹暾归_是的結(jié)果是很難讓人預(yù)料的。而且雖然
,宏的執(zhí)行很快(因?yàn)闆]有函數(shù)調(diào)用的開銷),但宏會(huì)讓源代碼澎漲,使目標(biāo)文件尺寸變
大,(如:一個(gè)50行的宏,程序中有1000個(gè)地方用到,宏展開后會(huì)很不得了),相反不能
讓程序執(zhí)行得更快(因?yàn)閳?zhí)行文件變大,運(yùn)行時(shí)系統(tǒng)換頁(yè)頻繁)。
?
因此,在決定是用函數(shù),還是用宏時(shí)得要小心。
--
23、static的使用
————————
static關(guān)鍵字,表示了“靜態(tài)”,一般來說,他會(huì)被經(jīng)常用于變量和函數(shù)。一個(gè)static的
變量,其實(shí)就是全局變量,只不過他是有作用域的全局變量。比如一個(gè)函數(shù)中的static變
量:
?
char*
getConsumerName()
{
??? static int cnt = 0;
?
??? ....
??? cnt++;
??? ....
}
?
?
cnt變量的值會(huì)跟隨著函數(shù)的調(diào)用次而遞增,函數(shù)退出后,cnt的值還存在,只是cnt只能在
函數(shù)中才能被訪問。而cnt的內(nèi)存也只會(huì)在函數(shù)第一次被調(diào)用時(shí)才會(huì)被分配和初始化,以后
每次進(jìn)入函數(shù),都不為static分配了,而直接使用上一次的值。
?
對(duì)于一些被經(jīng)常調(diào)用的函數(shù)內(nèi)的常量,最好也聲明成static(參見第12條)
?
但static的最多的用處卻不在這里,其最大的作用的控制訪問,在C中如果一個(gè)函數(shù)或是一
個(gè)全局變量被聲明為static,那么,這個(gè)函數(shù)和這個(gè)全局變量,將只能在這個(gè)C文件中被訪
問,如果別的C文件中調(diào)用這個(gè)C文件中的函數(shù),或是使用其中的全局(用extern關(guān)鍵字)
,將會(huì)發(fā)生鏈接時(shí)錯(cuò)誤。這個(gè)特性可以用于數(shù)據(jù)和程序保密。
?
?
?
24、函數(shù)中的代碼尺寸
——————————
一個(gè)函數(shù)完成一個(gè)具體的功能,一般來說,一個(gè)函數(shù)中的代碼最好不要超過600行左右,越
少越好,最好的函數(shù)一般在100行以內(nèi),300行左右的孫函數(shù)就差不多了。有證據(jù)表明,一
個(gè)函數(shù)中的代碼如果超過500行,就會(huì)有和別的函數(shù)相同或是相近的代碼,也就是說,就可
以再寫另一個(gè)函數(shù)。
?
另外,函數(shù)一般是完成一個(gè)特定的功能,千萬忌諱在一個(gè)函數(shù)中做許多件不同的事。函數(shù)
的功能越單一越好,一方面有利于函數(shù)的易讀性,另一方面更有利于代碼的維護(hù)和重用,
功能越單一表示這個(gè)函數(shù)就越可能給更多的程序提供服務(wù),也就是說共性就越多。
?
雖然函數(shù)的調(diào)用會(huì)有一定的開銷,但比起軟件后期維護(hù)來說,增加一些運(yùn)行時(shí)的開銷而換
來更好的可維護(hù)性和代碼重用性,是很值得的一件事。
?
?
25、typedef的使用
—————————
?
typedef是一個(gè)給類型起別名的關(guān)鍵字。不要小看了它,它對(duì)于你代碼的維護(hù)會(huì)有很好的作
用。比如C中沒有bool,于是在一個(gè)軟件中,一些程序員使用int,一些程序員使用short,
會(huì)比較混亂,最好就是用一個(gè)typedef來定義,如:
?
??? typedef char bool;
?
一般來說,一個(gè)C的工程中一定要做一些這方面的工作,因?yàn)槟銜?huì)涉及到跨平臺(tái),不同的平
臺(tái)會(huì)有不同的字長(zhǎng),所以利用預(yù)編譯和typedef可以讓你最有效的維護(hù)你的代碼,如下所示
:
?
??? #ifdef SOLARIS2_5
????? typedef boolean_t???? BOOL_T;
??? #else
??? #else
????? typedef int?????????? BOOL_T;
??? #endif
?
??? typedef short?????????? INT16_T;
??? typedef unsigned short? UINT16_T;
??? typedef int???????????? INT32_T;
??? typedef unsigned int??? UINT32_T;
?
??? #ifdef WIN32
????? typedef _int64??????? INT64_T;
??? #else
????? typedef long long???? INT64_T;
??? #endif
?
??? typedef float?????????? FLOAT32_T;
??? typedef char*?????????? STRING_T;
??? typedef unsigned char?? BYTE_T;
??? typedef time_t????????? TIME_T;
??? typedef INT32_T???????? PID_T;
?
使用typedef的其它規(guī)范是,在結(jié)構(gòu)和函數(shù)指針時(shí),也最好用typedef,這也有利于程序的
易讀和可維護(hù)性。如:
?
雖然函數(shù)的調(diào)用會(huì)有一定的開銷,但比起軟件后期維護(hù)來說,增加一些運(yùn)行時(shí)的開銷而換
來更好的可維護(hù)性和代碼重用性,是很值得的一件事。
?
?
25、typedef的使用
—————————
?
typedef是一個(gè)給類型起別名的關(guān)鍵字。不要小看了它,它對(duì)于你代碼的維護(hù)會(huì)有很好的作
用。比如C中沒有bool,于是在一個(gè)軟件中,一些程序員使用int,一些程序員使用short,
會(huì)比較混亂,最好就是用一個(gè)typedef來定義,如:
?
??? typedef char bool;
?
一般來說,一個(gè)C的工程中一定要做一些這方面的工作,因?yàn)槟銜?huì)涉及到跨平臺(tái),不同的平
臺(tái)會(huì)有不同的字長(zhǎng),所以利用預(yù)編譯和typedef可以讓你最有效的維護(hù)你的代碼,如下所示
:
?
??? #ifdef SOLARIS2_5
????? typedef boolean_t???? BOOL_T;
??? #else
??? #else
????? typedef int?????????? BOOL_T;
??? #endif
?
??? typedef short?????????? INT16_T;
??? typedef unsigned short? UINT16_T;
??? typedef int???????????? INT32_T;
??? typedef unsigned int??? UINT32_T;
?
??? #ifdef WIN32
????? typedef _int64??????? INT64_T;
??? #else
????? typedef long long???? INT64_T;
??? #endif
?
??? typedef float?????????? FLOAT32_T;
??? typedef char*?????????? STRING_T;
??? typedef unsigned char?? BYTE_T;
??? typedef time_t????????? TIME_T;
??? typedef INT32_T???????? PID_T;
?
使用typedef的其它規(guī)范是,在結(jié)構(gòu)和函數(shù)指針時(shí),也最好用typedef,這也有利于程序的
易讀和可維護(hù)性。如:
??? typedef struct _hostinfo {
??????? HOSTID_T?? host;
??????? INT32_T??? hostId;
??????? STRING_T?? hostType;
??????? STRING_T?? hostModel;
??????? FLOAT32_T? cpuFactor;
??????? INT32_T??? numCPUs;
??????? INT32_T??? nDisks;
??????? INT32_T??? memory;
??????? INT32_T??? swap;
??? } HostInfo;
?
?
??? typedef INT32_T (*RsrcReqHandler)(
???? void *info,
???? JobArray *jobs,
???? AllocInfo *allocInfo,
???? AllocList *allocList);
?
C++中這樣也是很讓人易讀的:
?
?
??? typedef CArray<HostInfo, HostInfo&> HostInfoArray;
?
于是,當(dāng)我們用其定義變量時(shí),會(huì)顯得十分易讀。如:
?
??? HostInfo* phinfo;
??? RsrcReqHandler* pRsrcHand;
?
這種方式的易讀性,在函數(shù)的參數(shù)中十分明顯。
?
關(guān)鍵是在程序種使用typedef后,幾乎所有的程序中的類型聲明都顯得那么簡(jiǎn)潔和清淅,而
且易于維護(hù),這才是typedef的關(guān)鍵。
?
?
?
26、為常量聲明宏
————————
最好不要在程序中出現(xiàn)數(shù)字式的“硬編碼”,如:
?
??? int user[120];
?
為這個(gè)120聲明一個(gè)宏吧。為所有出現(xiàn)在程序中的這樣的常量都聲明一個(gè)
宏吧。比如TimeOut的時(shí)間,最大的用戶數(shù)量,還有其它,只要是常量就應(yīng)該聲明成宏。如
果,突然在程序中出現(xiàn)下面一段代碼,
?
??? for ( i=0; i<120; i++){
??????? ....
??? }
?
120是什么?為什么會(huì)是120?這種“硬編碼”不僅讓程序很讀,而且也讓程序很不好維護(hù)
,如果要改變這個(gè)數(shù)字,得同時(shí)對(duì)所有程序中這個(gè)120都要做修改,這對(duì)修改程序的人來說
是一個(gè)很大的痛苦。所以還是把常量聲明成宏,這樣,一改百改,而且也很利于程序閱讀
。
?
??? #define MAX_USR_CNT 120
?
??? for ( i=0; i<MAX_USER_CNT; i++){
??????? ....
??? }
?
這樣就很容易了解這段程序的意圖了。
?
有的程序員喜歡為這種變量聲明全局變量,其實(shí),全局變量應(yīng)該盡量的少用,全局變量不
利于封裝,也不利于維護(hù),而且對(duì)程序執(zhí)行空間有一定的開銷,一不小心就造成系統(tǒng)換頁(yè)
,造成程序執(zhí)行速度效率等問題。所以聲明成宏,即可以免去全局變量的開銷,也會(huì)有速
,造成程序執(zhí)行速度效率等問題。所以聲明成宏,即可以免去全局變量的開銷,也會(huì)有速
度上的優(yōu)勢(shì)。
?
?
27、不要為宏定義加分號(hào)
———————————
?
有許多程序員不知道在宏定義時(shí)是否要加分號(hào),有時(shí),他們以為宏是一條語(yǔ)句,應(yīng)該要加
分號(hào),這就錯(cuò)了。當(dāng)你知道了宏的原理,你會(huì)贊同我為會(huì)么不要為宏定義加分號(hào)的。看一
個(gè)例子:
?
??? #define MAXNUM 1024;
?
這是一個(gè)有分號(hào)的宏,如果我們這樣使用:
?
??? half = MAXNUM/2;
?
??? if ( num < MAXNUM )
?
等等,都會(huì)造成程序的編譯錯(cuò)誤,因?yàn)椋?dāng)宏展開后,他會(huì)是這個(gè)樣子的:
?
??? half = 1024;/2;
?
?
??? if ( num < 1024; )
?
是的,分號(hào)也被展進(jìn)去了,所以造成了程序的錯(cuò)誤。請(qǐng)相信我,有時(shí)候,一個(gè)分號(hào)會(huì)讓你
的程序出現(xiàn)成百個(gè)錯(cuò)誤。所以還是不要為宏加最后一個(gè)分號(hào),哪怕是這樣:
?
??? #define LINE??? "================================="
?
??? #define PRINT_LINE? printf(LINE)
?
??? #define PRINT_NLINE(n)? while ( n-- >0 ) { PRINT_LINE; }
?
都不要在最后加上分號(hào),當(dāng)我們?cè)诔绦蛑惺褂脮r(shí),為之加上分號(hào),
?
??? main()
??? {
??????? char *p = LINE;
??????? PRINT_LINE;
??? }
?
這一點(diǎn)非常符合習(xí)慣,而且,如果忘加了分號(hào),編譯器給出的錯(cuò)誤提示,也會(huì)讓我們很容
易看懂的。
--
?????????????????? 開始使勁
??? #define PRINT_NLINE(n)? while ( n-- >0 ) { PRINT_LINE; }
?
都不要在最后加上分號(hào),當(dāng)我們?cè)诔绦蛑惺褂脮r(shí),為之加上分號(hào),
?
??? main()
??? {
??????? char *p = LINE;
??????? PRINT_LINE;
??? }
?
這一點(diǎn)非常符合習(xí)慣,而且,如果忘加了分號(hào),編譯器給出的錯(cuò)誤提示,也會(huì)讓我們很容
易看懂的。
--
28、||和&&的語(yǔ)句執(zhí)行順序
————————————
條件語(yǔ)句中的這兩個(gè)“與”和“或”操作符一定要小心,它們的表現(xiàn)可能和你想像的不一
樣,這里條件語(yǔ)句中的有些行為需要和說一下:
?
??? express1 || express2
?
??? 先執(zhí)行表達(dá)式express1如果為“真”,express2將不被執(zhí)行,express2僅在express1
為“假”時(shí)才被執(zhí)行。因?yàn)榈谝粋€(gè)表達(dá)式為真了,整個(gè)表達(dá)式都為真,所以沒有必要再去
執(zhí)行第二個(gè)表達(dá)式了。
?
??? express1 && express2
?
??? 先執(zhí)行表達(dá)式express1如果為“假”,express2將不被執(zhí)行,express2僅在express1
為“真”時(shí)才被執(zhí)行。因?yàn)榈谝粋€(gè)表達(dá)式為假了,整個(gè)表達(dá)式都為假了,所以沒有必要再
去執(zhí)行第二個(gè)表達(dá)式了。
?
??? for ( i=0; i<120; i++){
??????? ....
??? }
?
120是什么?為什么會(huì)是120?這種“硬編碼”不僅讓程序很讀,而且也讓程序很不好維護(hù)
,如果要改變這個(gè)數(shù)字,得同時(shí)對(duì)所有程序中這個(gè)120都要做修改,這對(duì)修改程序的人來說
是一個(gè)很大的痛苦。所以還是把常量聲明成宏,這樣,一改百改,而且也很利于程序閱讀
。
?
??? #define MAX_USR_CNT 120
?
??? for ( i=0; i<MAX_USER_CNT; i++){
??????? ....
??? }
?
這樣就很容易了解這段程序的意圖了。
?
有的程序員喜歡為這種變量聲明全局變量,其實(shí),全局變量應(yīng)該盡量的少用,全局變量不
利于封裝,也不利于維護(hù),而且對(duì)程序執(zhí)行空間有一定的開銷,一不小心就造成系統(tǒng)換頁(yè)
,造成程序執(zhí)行速度效率等問題。所以聲明成宏,即可以免去全局變量的開銷,也會(huì)有速
,造成程序執(zhí)行速度效率等問題。所以聲明成宏,即可以免去全局變量的開銷,也會(huì)有速
度上的優(yōu)勢(shì)。
?
?
27、不要為宏定義加分號(hào)
———————————
?
有許多程序員不知道在宏定義時(shí)是否要加分號(hào),有時(shí),他們以為宏是一條語(yǔ)句,應(yīng)該要加
分號(hào),這就錯(cuò)了。當(dāng)你知道了宏的原理,你會(huì)贊同我為會(huì)么不要為宏定義加分號(hào)的。看一
個(gè)例子:
?
??? #define MAXNUM 1024;
?
這是一個(gè)有分號(hào)的宏,如果我們這樣使用:
?
??? half = MAXNUM/2;
?
??? if ( num < MAXNUM )
?
等等,都會(huì)造成程序的編譯錯(cuò)誤,因?yàn)椋?dāng)宏展開后,他會(huì)是這個(gè)樣子的:
?
??? half = 1024;/2;
?
?
??? if ( num < 1024; )
?
是的,分號(hào)也被展進(jìn)去了,所以造成了程序的錯(cuò)誤。請(qǐng)相信我,有時(shí)候,一個(gè)分號(hào)會(huì)讓你
的程序出現(xiàn)成百個(gè)錯(cuò)誤。所以還是不要為宏加最后一個(gè)分號(hào),哪怕是這樣:
?
??? #define LINE??? "================================="
?
??? #define PRINT_LINE? printf(LINE)
?
??? #define PRINT_NLINE(n)? while ( n-- >0 ) { PRINT_LINE; }
?
都不要在最后加上分號(hào),當(dāng)我們?cè)诔绦蛑惺褂脮r(shí),為之加上分號(hào),
?
??? main()
??? {
??????? char *p = LINE;
??????? PRINT_LINE;
??? }
?
這一點(diǎn)非常符合習(xí)慣,而且,如果忘加了分號(hào),編譯器給出的錯(cuò)誤提示,也會(huì)讓我們很容
易看懂的。
--
?????????????????? 開始使勁
??? #define PRINT_NLINE(n)? while ( n-- >0 ) { PRINT_LINE; }
?
都不要在最后加上分號(hào),當(dāng)我們?cè)诔绦蛑惺褂脮r(shí),為之加上分號(hào),
?
??? main()
??? {
??????? char *p = LINE;
??????? PRINT_LINE;
??? }
?
這一點(diǎn)非常符合習(xí)慣,而且,如果忘加了分號(hào),編譯器給出的錯(cuò)誤提示,也會(huì)讓我們很容
易看懂的。
--
28、||和&&的語(yǔ)句執(zhí)行順序
————————————
條件語(yǔ)句中的這兩個(gè)“與”和“或”操作符一定要小心,它們的表現(xiàn)可能和你想像的不一
樣,這里條件語(yǔ)句中的有些行為需要和說一下:
?
??? express1 || express2
?
??? 先執(zhí)行表達(dá)式express1如果為“真”,express2將不被執(zhí)行,express2僅在express1
為“假”時(shí)才被執(zhí)行。因?yàn)榈谝粋€(gè)表達(dá)式為真了,整個(gè)表達(dá)式都為真,所以沒有必要再去
執(zhí)行第二個(gè)表達(dá)式了。
?
??? express1 && express2
?
??? 先執(zhí)行表達(dá)式express1如果為“假”,express2將不被執(zhí)行,express2僅在express1
為“真”時(shí)才被執(zhí)行。因?yàn)榈谝粋€(gè)表達(dá)式為假了,整個(gè)表達(dá)式都為假了,所以沒有必要再
去執(zhí)行第二個(gè)表達(dá)式了。
?
于是,他并不是你所想像的所有的表達(dá)式都會(huì)去執(zhí)行,這點(diǎn)一定要明白,不然你的程序會(huì)
出現(xiàn)一些莫明的運(yùn)行時(shí)錯(cuò)誤。
?
例如,下面的程序:
?
?
??? if ( sum > 100 &&
???????? ( ( fp=fopen( filename,"a" ) ) != NULL )?? {
?
???????? fprintf(fp, "Warring: it beyond one hundred\n");
???????? ......
??? }
?
??? fprintf( fp, " sum is %id \n", sum );
??? fclose( fp );
?
本來的意圖是,如果sum > 100 ,向文件中寫一條出錯(cuò)信息,為了方便,把兩個(gè)條件判斷
寫在一起,于是,如果sum<=100時(shí),打開文件的操作將不會(huì)做,最后,fprintf和fclose就
會(huì)發(fā)現(xiàn)未知的結(jié)果。
?
?
再比如,如果我想判斷一個(gè)字符是不是有內(nèi)容,我得判斷這個(gè)字符串指針是不為空(NULL
)并且其內(nèi)容不能為空(Empty),一個(gè)是空指針,一個(gè)是空內(nèi)容。我也許會(huì)這樣寫:
if ( ( p != NULL ) && ( strlen(p) != 0 ))
于是,如果p為NULL,那么strlen(p)就不會(huì)被執(zhí)行,于是,strlen也就不會(huì)因?yàn)橐粋€(gè)空指
針而“非法操作”或是一個(gè)“Core Dump”了。
針而“非法操作”或是一個(gè)“Core Dump”了。
記住一點(diǎn),條件語(yǔ)句中,并非所有的語(yǔ)句都會(huì)執(zhí)行,當(dāng)你的條件語(yǔ)句非常多時(shí),這點(diǎn)要尤
其注意。
其注意。
?
29、盡量用for而不是while做循環(huán)
———————————————
基本上來說,for可以完成while的功能,我是建議盡量使用for語(yǔ)句,而不要使用while語(yǔ)
句,特別是當(dāng)循環(huán)體很大時(shí),for的優(yōu)點(diǎn)一下就體現(xiàn)出來了。
———————————————
基本上來說,for可以完成while的功能,我是建議盡量使用for語(yǔ)句,而不要使用while語(yǔ)
句,特別是當(dāng)循環(huán)體很大時(shí),for的優(yōu)點(diǎn)一下就體現(xiàn)出來了。
因?yàn)樵趂or中,循環(huán)的初始、結(jié)束條件、循環(huán)的推進(jìn),都在一起,一眼看上去就知道這是一
個(gè)什么樣的循環(huán)。剛出學(xué)校的程序一般對(duì)于鏈接喜歡這樣來:
個(gè)什么樣的循環(huán)。剛出學(xué)校的程序一般對(duì)于鏈接喜歡這樣來:
p = pHead;
p = pHead;
p = pHead;
while ( p ){
...
...
p = p->next;
}
...
...
p = p->next;
}
當(dāng)while的語(yǔ)句塊變大后,你的程序?qū)⒑茈y讀,用for就好得多:
for ( p=pHead; p; p=p->next ){
..
}
..
}
一眼就知道這個(gè)循環(huán)的開始條件,結(jié)束條件,和循環(huán)的推進(jìn)。大約就能明白這個(gè)循環(huán)要做
個(gè)什么事?而且,程序維護(hù)進(jìn)來很容易,不必像while一樣,在一個(gè)編輯器中上上下下的搗
騰。
個(gè)什么事?而且,程序維護(hù)進(jìn)來很容易,不必像while一樣,在一個(gè)編輯器中上上下下的搗
騰。
?
30、請(qǐng)sizeof類型而不是變量
—————————————
—————————————
許多程序員在使用sizeof中,喜歡sizeof變量名,例如:
int score[100];
char filename[20];
struct UserInfo usr[100];
char filename[20];
struct UserInfo usr[100];
在sizeof這三個(gè)的變量名時(shí),都會(huì)返回正確的結(jié)果,于是許多程序員就開始sizeof變量名
。這個(gè)習(xí)慣很雖然沒有什么不好,但我還是建議sizeof類型。
。這個(gè)習(xí)慣很雖然沒有什么不好,但我還是建議sizeof類型。
我看到過這個(gè)的程序:
pScore = (int*) malloc( SUBJECT_CNT );
memset( pScore, 0, sizeof(pScore) );
...
memset( pScore, 0, sizeof(pScore) );
...
此時(shí),sizeof(pScore)返回的就是4(指針的長(zhǎng)度),不會(huì)是整個(gè)數(shù)組,于是,memset就不
能對(duì)這塊內(nèi)存進(jìn)行初始化。為了程序的易讀和易維護(hù),我強(qiáng)烈建議使用類型而不是變量,
如:
能對(duì)這塊內(nèi)存進(jìn)行初始化。為了程序的易讀和易維護(hù),我強(qiáng)烈建議使用類型而不是變量,
如:
對(duì)于score: sizeof(int) * 100 /* 100個(gè)int */
對(duì)于filename: sizeof(char) * 20 /* 20個(gè)char */
對(duì)于usr: sizeof(struct UserInfo) * 100 /* 100個(gè)UserInfo */
對(duì)于filename: sizeof(char) * 20 /* 20個(gè)char */
對(duì)于usr: sizeof(struct UserInfo) * 100 /* 100個(gè)UserInfo */
這樣的代碼是不是很易讀?一眼看上去就知道什么意思了。
另外一點(diǎn),sizeof一般用于分配內(nèi)存,這個(gè)特性特別在多維數(shù)組時(shí),就能體現(xiàn)出其優(yōu)點(diǎn)了
。如,給一個(gè)字符串?dāng)?shù)組分配內(nèi)存,
/*
* 分配一個(gè)有20個(gè)字符串,
* 每個(gè)字符串長(zhǎng)100的內(nèi)存
*/
* 分配一個(gè)有20個(gè)字符串,
* 每個(gè)字符串長(zhǎng)100的內(nèi)存
*/
char* *p;
/*
* 錯(cuò)誤的分配方法
*/
p = (char**)calloc( 20*100, sizeof(char) );
* 錯(cuò)誤的分配方法
*/
p = (char**)calloc( 20*100, sizeof(char) );
/*
* 正確的分配方法
* 正確的分配方法
*/
p = (char**) calloc ( 20, sizeof(char*) );
for ( i=0; i<20; i++){
/*p = (char*) calloc ( 100, sizeof(char) );*/
p[i] = (char*) calloc ( 100, sizeof(char) );
}
(注:上述語(yǔ)句被注釋掉的是原來的,是錯(cuò)誤的,由dasherest朋友指正,謝謝)
為了代碼的易讀,省去了一些判斷,請(qǐng)注意這兩種分配的方法,有本質(zhì)上的差別。
?
31、不要忽略Warning
——————————
對(duì)于一些編譯時(shí)的警告信息,請(qǐng)不要忽視它們。雖然,這些Warning不會(huì)妨礙目標(biāo)代碼的生
成,但這并不意味著你的程序就是好的。必竟,并不是編譯成功的程序才是正確的,編譯
成功只是萬里長(zhǎng)征的第一步,后面還有大風(fēng)大浪在等著你。從編譯程序開始,不但要改正
每個(gè)error,還要修正每個(gè)warning。這是一個(gè)有修養(yǎng)的程序員該做的事。
——————————
對(duì)于一些編譯時(shí)的警告信息,請(qǐng)不要忽視它們。雖然,這些Warning不會(huì)妨礙目標(biāo)代碼的生
成,但這并不意味著你的程序就是好的。必竟,并不是編譯成功的程序才是正確的,編譯
成功只是萬里長(zhǎng)征的第一步,后面還有大風(fēng)大浪在等著你。從編譯程序開始,不但要改正
每個(gè)error,還要修正每個(gè)warning。這是一個(gè)有修養(yǎng)的程序員該做的事。
一般來說,一面的一些警告信息是常見的:
1)聲明了未使用的變量。(雖然編譯器不會(huì)編譯這種變量,但還是把它從源程序中注
釋或是刪除吧)
2)使用了隱晦聲明的函數(shù)。(也許這個(gè)函數(shù)在別的C文件中,編譯時(shí)會(huì)出現(xiàn)這種警告
,你應(yīng)該這使用之前使用extern關(guān)鍵字聲明這個(gè)函數(shù))
3)沒有轉(zhuǎn)換一個(gè)指針。(例如malloc返回的指針是void的,你沒有把之轉(zhuǎn)成你實(shí)際類
型而報(bào)警,還是手動(dòng)的在之前明顯的轉(zhuǎn)換一下吧)
4)類型向下轉(zhuǎn)換。(例如:float f = 2.0; 這種語(yǔ)句是會(huì)報(bào)警告的,編譯會(huì)告訴你
正試圖把一個(gè)double轉(zhuǎn)成float,你正在閹割一個(gè)變量,你真的要這樣做嗎?還是在2.0后
面加個(gè)f吧,不然,2.0就是一個(gè)double,而不是float了)
不管怎么說,編譯器的Warning不要小視,最好不要忽略,一個(gè)程序都做得出來,何況幾個(gè)
小小的Warning呢?
小小的Warning呢?
?
32、書寫Debug版和Release版的程序
————————————————
程序在開發(fā)過程中必然有許多程序員加的調(diào)試信息。我見過許多項(xiàng)目組,當(dāng)程序開發(fā)結(jié)束
時(shí),發(fā)動(dòng)群眾刪除程序中的調(diào)試信息,何必呢?為什么不像VC++那樣建立兩個(gè)版本的目標(biāo)
代碼?一個(gè)是debug版本的,一個(gè)是Release版的。那些調(diào)試信息是那么的寶貴,在日后的
維護(hù)過程中也是很寶貴的東西,怎么能說刪除就刪除呢?
————————————————
程序在開發(fā)過程中必然有許多程序員加的調(diào)試信息。我見過許多項(xiàng)目組,當(dāng)程序開發(fā)結(jié)束
時(shí),發(fā)動(dòng)群眾刪除程序中的調(diào)試信息,何必呢?為什么不像VC++那樣建立兩個(gè)版本的目標(biāo)
代碼?一個(gè)是debug版本的,一個(gè)是Release版的。那些調(diào)試信息是那么的寶貴,在日后的
維護(hù)過程中也是很寶貴的東西,怎么能說刪除就刪除呢?
利用預(yù)編譯技術(shù)吧,如下所示聲明調(diào)試函數(shù):
#ifdef DEBUG
void TRACE(char* fmt, ...)
{
......
}
#else
#define TRACE(char* fmt, ...)
#endif
于是,讓所有的程序都用TRACE輸出調(diào)試信息,只需要在在編譯時(shí)加上一個(gè)參數(shù)“-DDEBUG
”,如:
”,如:
cc -DDEBUG -o target target.c
于是,預(yù)編譯器發(fā)現(xiàn)DEBUG變量被定義了,就會(huì)使用TRACE函數(shù)。而如果要發(fā)布給用戶了,
那么只需要把取消“-DDEBUG”的參數(shù),于是所有用到TRACE宏,這個(gè)宏什么都沒有,所以
源程序中的所有TRACE語(yǔ)言全部被替換成了空。一舉兩得,一箭雙雕,何樂而不為呢?
那么只需要把取消“-DDEBUG”的參數(shù),于是所有用到TRACE宏,這個(gè)宏什么都沒有,所以
源程序中的所有TRACE語(yǔ)言全部被替換成了空。一舉兩得,一箭雙雕,何樂而不為呢?
順便提一下,兩個(gè)很有用的系統(tǒng)宏,一個(gè)是“__FILE__”,一個(gè)是“__LINE__”,分別表
示,所在的源文件和行號(hào),當(dāng)你調(diào)試信息或是輸出錯(cuò)誤時(shí),可以使用這兩個(gè)宏,讓你一眼
就能看出你的錯(cuò)誤,出現(xiàn)在哪個(gè)文件的第幾行中。這對(duì)于用C/C++做的大工程非常的管用。
示,所在的源文件和行號(hào),當(dāng)你調(diào)試信息或是輸出錯(cuò)誤時(shí),可以使用這兩個(gè)宏,讓你一眼
就能看出你的錯(cuò)誤,出現(xiàn)在哪個(gè)文件的第幾行中。這對(duì)于用C/C++做的大工程非常的管用。
綜上所述32條,都是為了三大目的——
1、?程序代碼的易讀性。
2、程序代碼的可維護(hù)性,
3、程序代碼的穩(wěn)定可靠性
2、程序代碼的可維護(hù)性,
3、程序代碼的穩(wěn)定可靠性
的細(xì)小的問題,編程高手不僅技術(shù)要強(qiáng),基礎(chǔ)要好,而且最重要的是要有“修養(yǎng)”!
?
軟件的維護(hù)有大量的工作量花在代碼的維護(hù)上,軟件的Upgrade,也有大量的工作花在代碼的組織上,所以好的代碼,清淅的,易讀的代碼,將給大大減少軟件的維護(hù)和升級(jí)成本。