編程修養(一)

什麼是好的程序員?是不是懂得很多技術細節?還是懂底層編程?還是編程速度比較快?
我覺得都不是。對於一些技術細節來說和底層的技術,只要看幫助,查資料就能找到,對
於速度快,只要編得多也就熟能生巧了。
 
我認爲好的程序員應該有以下幾方面的素質:
 
  1、有專研精神,勤學善問、舉一反三。
  2、積極向上的態度,有創造性思維。
  3、與人積極交流溝通的能力,有團隊精神。
  4、謙虛謹慎,戒驕戒燥。
  5、寫出的代碼質量高。包括:代碼的穩定、易讀、規範、易維護、專業。
 
這些都是程序員的修養,這裏我想談談“編程修養”,也就是上述中的第5點。我覺得,如

果我要了解一個作者,我會看他所寫的小說,如果我要了解一個畫家,我會看他所畫的圖
畫,如果我要了解一個工人,我會看他所做出來的產品,同樣,如果我要了解一個程序員
,我想首先我最想看的就是他的程序代碼,程序代碼可以看出一個程序員的素質和修養,
程序就像一個作品,有素質有修養的程序員的作品必然是一圖精美的圖畫,一首美妙的歌
曲,一本賞心悅目的小說。
 
我看過許多程序,沒有註釋,沒有縮進,胡亂命名的變量名,等等,等等,我把這種人統
稱爲沒有修養的程序,這種程序員,是在做創造性的工作嗎?不,完全就是在搞破壞,他
們與其說是在編程,還不如說是在對源程序進行“加密”,這種程序員,見一個就應該開
除一個,因爲他編的程序所創造的價值,遠遠小於需要在上面進行維護的價值。
 
程序員應該有程序員的修養,那怕再累,再沒時間,也要對自己的程序負責。我寧可要那
種動作慢,技術一般,但有良好的寫程序風格的程序員,也不要那種技術強、動作快的“
搞破壞”的程序員。有句話叫“字如其人”,我想從程序上也能看出一個程序員的優劣。
因爲,程序是程序員的作品,作品的好壞直截關係到程序員的聲譽和素質。而“修養”好
的程序員一定能做出好的程序和軟件。
 
有個成語叫“獨具匠心”,意思是做什麼都要做得很專業,很用心,如果你要做一個“匠
”,也就是造詣高深的人,那麼,從一件很簡單的作品上就能看出你有沒有“匠”的特性
,我覺得做一個程序員不難,但要做一個“程序匠”就不簡單了。編程序很簡單,但編出
有質量的程序就難了。
 
 
我在這裏不討論過深的技術,我只想在一些容易讓人忽略的東西上說一說,雖然這些東西
可能很細微,但如果你不注意這些細微之處的話,那麼他將會極大的影響你的整個軟件質
量,以及整個軟件程的實施,所謂“千里之堤,毀於蟻穴”。
 
“細微之處見真功”,真正能體現一個程序的功底恰恰在這些細微之處。
 
這就是程序員的——編程修養。我總結了在用C/C++語言(主要是C語言)進行程序寫作上
的三十二個“修養”,通過這些,你可以寫出質量高的程序,同時也會讓看你程序的人漬
漬稱道,那些看過你程序的人一定會說:“這個人的編程修養不錯”。
 
    ————————————————————————
 
        01、版權和版本
        02、縮進、空格、換行、空行、對齊
        03、程序註釋
        04、函數的[in][out]參數
        05、對系統調用的返回進行判斷
        06、if 語句對出錯的處理
        07、頭文件中的#ifndef
        08、在堆上分配內存
        09、變量的初始化
        10、h和c文件的使用

        11、出錯信息的處理
        12、常用函數和循環語句中的被計算量
        13、函數名和變量名的命名
        14、函數的傳值和傳指針
        15、修改別人程序的修養
        16、把相同或近乎相同的代碼形成函數和宏
        17、表達式中的括號
        18、函數參數中的const
        19、函數的參數個數
        20、函數的返回類型,不要省略
        21、goto語句的使用
        22、宏的使用
        23、static的使用
        24、函數中的代碼尺寸
        25、typedef的使用
        26、爲常量聲明宏
        27、不要爲宏定義加分號
        28、||和&&的語句執行順序
        29、儘量用for而不是while做循環
        30、請sizeof類型而不是變量
        31、不要忽略Warning
        32、書寫Debug版和Release版的程序

1、版權和版本
———————
好的程序員會給自己的每個函數,每個文件,都註上版權和版本。
 
對於C/C++的文件,文件頭應該有類似這樣的註釋:
/************************************************************************
*
*   文件名:network.c
*
*   文件描述:網絡通訊函數集
*
*   創建人: Hao Chen, 2003年2月3日
*
*   版本號:1.0
*
*   修改記錄:
*
*
************************************************************************/
 
而對於函數來說,應該也有類似於這樣的註釋:
 
/*================================================================
*
* 函 數 名:XXX
*
* 參    數:
*
*        type name [IN] : descripts
*
* 功能描述:
*
*        ..............
*
* 返 回 值:成功TRUE,失敗FALSE
*
* 拋出異常:
*
* 作    者:ChenHao 2003/4/2
*
*
================================================================*/
 
這樣的描述可以讓人對一個函數,一個文件有一個總體的認識,對代碼的易讀性和易維護
性有很大的好處。這是好的作品產生的開始。
 
 
 
2、縮進、空格、換行、空行、對齊
————————————————
i) 縮進應該是每個程序都會做的,只要學程序過程序就應該知道這個,但是我仍然看過不
縮進的程序,或是亂縮進的程序,如果你的公司還有寫程序不縮進的程序員,請毫不猶豫
的開除他吧,並以破壞源碼罪起訴他,還要他賠償讀過他程序的人的精神損失費。縮進,
這是不成文規矩,我再重提一下吧,一個縮進一般是一個TAB鍵或是4個空格。(最好用TAB
鍵)
 
ii) 空格。空格能給程序代來什麼損失嗎?沒有,有效的利用空格可以讓你的程序讀進來
更加賞心悅目。而不一堆表達式擠在一起。看看下面的代碼:
 
    ha=(ha*128+*key++)%tabPtr->size;
 
    ha = ( ha * 128 + *key++ ) % tabPtr->size;
 
 
    有空格和沒有空格的感覺不一樣吧。一般來說,語句中要在各個操作符間加空格,函
數調用時,要以各個參數間加空格。如下面這種加空格的和不加的:
 
if ((hProc=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid))==NULL){
}
 
if ( ( hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid) ) == NULL ){
}
 
iii) 換行。不要把語句都寫在一行上,這樣很不好。如:
 
    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;
        }
    }
 
 
    好多了吧?有時候,函數參數多的時候,最好也換行,如:
CreateProcess(
                  NULL,
                  cmdbuf,
                  NULL,
                  NULL,
                  bInhH,
                  dwCrtFlags,
                  envbuf,
                  NULL,
                  &siStartInfo,
                  &prInfo
                 );
 
    條件語句也應該在必要時換行:
 
    if ( ch >= '0' || ch <= '9' ||
         ch >= 'a' || ch <= 'z' ||
         ch >= 'A' || ch <= 'Z' )
 
 
iv) 空行。不要不加空行,空行可以區分不同的程序塊,程序塊間,最好加上空行。如:

 
    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) 對齊。用TAB鍵對齊你的一些變量的聲明或註釋,一樣會讓你的程序好看一些。如:
 
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;
 
怎麼樣?感覺不錯吧。
 
這裏主要講述瞭如果寫出讓人賞心悅目的代碼,好看的代碼會讓人的心情愉快,讀起代碼
也就不累,工整、整潔的程序代碼,通常更讓人歡迎,也更讓人稱道。現在的硬盤空間這
麼大,不要讓你的代碼擠在一起,這樣它們會抱怨你虐待它們的。好了,用“縮進、空格
、換行、空行、對齊”裝飾你的代碼吧,讓他們從沒有秩序的土匪中變成一排排整齊有秩
序的正規部隊吧。
 
 
 
 
3、程序註釋
3、程序註釋
——————
養成寫程序註釋的習慣,這是每個程序員所必須要做的工作。我看過那種幾千行,卻居然
沒有一行註釋的程序。這就如同在公路上駕車卻沒有路標一樣。用不了多久,連自己都不
知道自己的意圖了,還要花上幾倍的時間纔看明白,這種浪費別人和自己的時間的人,是
最爲可恥的人。
 
是的,你也許會說,你會寫註釋,真的嗎?註釋的書寫也能看出一個程序員的功底。一般
來說你需要至少寫這些地方的註釋:文件的註釋、函數的註釋、變量的註釋、算法的註釋
、功能塊的程序註釋。主要就是記錄你這段程序是幹什麼的?你的意圖是什麼?你這個變
量是用來做什麼的?等等。
 
不要以爲註釋好寫,有一些算法是很難說或寫出來的,只能意會,我承認有這種情況的時
候,但你也要寫出來,正好可以訓練一下自己的表達能力。而表達能力正是那種悶頭搞技
術的技術人員最缺的,你有再高的技術,如果你表達能力不行,你的技術將不能得到充分
的發揮。因爲,這是一個團隊的時代。
 
好了,說幾個註釋的技術細節:
 
i) 對於行註釋(“//”)比塊註釋(“/* */”)要好的說法,我並不是很同意。因爲一
些老版本的C編譯器並不支持行註釋,所以爲了你的程序的移植性,請你還是儘量使用塊注
釋。
 
 
ii) 你也許會爲塊註釋的不能嵌套而不爽,那麼你可以用預編譯來完成這個功能。使用“#
if 0”和“#endif”括起來的代碼,將不被編譯,而且還可以嵌套。
 
 
 
 
4、函數的[in][out]參數
———————————
 
我經常看到這樣的程序:
FuncName(char* str)
{
    int len = strlen(str);
    .....
}
 
char*
GetUserName(struct user* pUser)
{
    return pUser->name;
}
 
 
不!請不要這樣做。
你應該先判斷一下傳進來的那個指針是不是爲空。如果傳進來的指針爲空的話,那麼,你
的一個大的系統就會因爲這一個小的函數而崩潰。一種更好的技術是使用斷言(assert)
,這裏我就不多說這些技術細節了。當然,如果是在C++中,引用要比指針好得多,但你也
需要對各個參數進行檢查。
 
寫有參數的函數時,首要工作,就是要對傳進來的所有參數進行合法性檢查。而對於傳出
的參數也應該進行檢查,這個動作當然應該在函數的外部,也就是說,調用完一個函數後
,應該對其傳出的值進行檢查。
 
當然,檢查會浪費一點時間,但爲了整個系統不至於出現“非法操作”或是“Core Dump”
的系統級的錯誤,多花這點時間還是很值得的。
 
 
 
 
5、對系統調用的返回進行判斷
——————————————
繼續上一條,對於一些系統調用,比如打開文件,我經常看到,許多程序員對fopen返回的
指針不做任何判斷,就直接使用了。然後發現文件的內容怎麼也讀出不,或是怎麼也寫不
進去。還是判斷一下吧:
 
 
    fp = fopen("log.txt", "a");
    if ( fp == NULL ){
        printf("Error: open file error/n");
        return FALSE;
    }
 
其它還有許多啦,比如:socket返回的socket號,malloc返回的內存。請對這些系統調用
返回的東西進行判斷。
 

發佈了11 篇原創文章 · 獲贊 6 · 訪問量 31萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章