<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    空間站

    北極心空

      BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
      15 Posts :: 393 Stories :: 160 Comments :: 0 Trackbacks
    編程修養 (作者:陳皓
    ————
    ?
    什么是好的程序員?是不是懂得很多技術細節?還是懂底層編程?還是編程速度比較快?
    我覺得都不是。對于一些技術細節來說和底層的技術,只要看幫助,查資料就能找到,對
    于速度快,只要編得多也就熟能生巧了。
    ?
    我認為好的程序員應該有以下幾方面的素質:
    ?
    ? 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版的程序
    ??????? 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返回的內存。請對這些系統調用
    返回的東西進行判斷。
    6、if 語句對出錯的處理
    ———————————
    我看見你說了,這有什么好說的。還是先看一段程序代碼吧。
    ?
    ??? if ( ch >= '0' && ch <= '9' ){
    ??????? /* 正常處理代碼 */
    ??? }else{
    ??????? /* 輸出錯誤信息 */
    ??????? printf("error ......\n");
    ??????? return ( FALSE );
    ??? }
    ?
    這種結構很不好,特別是如果“正常處理代碼”很長時,對于這種情況,最好不要用else
    。先判斷錯誤,如:
    ?
    ??? if ( ch < '0' || ch > '9' ){
    ??????? /* 輸出錯誤信息 */
    ??????? printf("error ......\n");
    ??????? return ( FALSE );
    ??? }
    ?
    ??? /* 正常處理代碼 */
    ??? ......
    ?
    ?
    這樣的結構,不是很清楚嗎?突出了錯誤的條件,讓別人在使用你的函數的時候,第一眼
    就能看到不合法的條件,于是就會更下意識的避免。
    ?
    ?
    ?
    ?
    7、頭文件中的#ifndef
    ——————————
    千萬不要忽略了頭件的中的#ifndef,這是一個很關鍵的東西。比如你有兩個C文件,這兩
    個C文件都include了同一個頭文件。而編譯時,這兩個C文件要一同編譯成一個可運行文件
    ,于是問題來了,大量的聲明沖突。
    ?
    還是把頭文件的內容都放在#ifndef和#endif中吧。不管你的頭文件會不會被多個文件引用
    管你的頭文件會不會被多個文件引用
    ,你都要加上這個。一般格式是這樣的:
    ?
    ??? #ifndef? <標識>
    ??? #define <標識>
    ?
    ??? ......
    ??? ......
    ?
    ??? #endif
    ?
    <標識>在理論上來說可以是自由命名的,但每個頭文件的這個“標識”都應該是唯一的。
    標識的命名規則一般是頭文件名全大寫,前后加下劃線,并把文件名中的“.”也變成下劃
    線,如:stdio.h
    ?
    ??? #ifndef _STDIO_H_
    ??? #define _STDIO_H_
    ?
    ??? ......
    ?
    ??? #endif
    ?
    (BTW:預編譯有多很有用的功能。你會用預編譯嗎?)
    (BTW:預編譯有多很有用的功能。你會用預編譯嗎?)
    ?
    ?
    ?
    ?
    8、在堆上分配內存
    —————————
    可能許多人對內存分配上的“棧 stack”和“堆 heap”還不是很明白。包括一些科班出身
    的人也不明白這兩個概念。我不想過多的說這兩個東西。簡單的來講,stack上分配的內存
    系統自動釋放,heap上分配的內存,系統不釋放,哪怕程序退出,那一塊內存還是在那里
    。stack一般是靜態分配內存,heap上一般是動態分配內存。
    ?
    由malloc系統函數分配的內存就是從堆上分配內存。從堆上分配的內存一定要自己釋放。
    用free釋放,不然就是術語——“內存泄露”(或是“內存漏洞”)—— Memory Leak。
    于是,系統的可分配內存會隨malloc越來越少,直到系統崩潰。還是來看看“棧內存”和
    “堆內存”的差別吧。
    ?
    ??? 棧內存分配
    ??? —————
    ??? char*
    ??? AllocStrFromStack()
    ??? {
    ??????? char pstr[100];
    ??????? return pstr;
    ??? }
    ?
    ?
    ??? 堆內存分配
    ??? —————
    ??? char*
    ??? AllocStrFromHeap(int len)
    ??? {
    ??????? char *pstr;
    ?
    ??????? if ( len <= 0 ) return NULL;
    ??????? return ( char* ) malloc( len );
    ??? }
    ?
    對于第一個函數,那塊pstr的內存在函數返回時就被系統釋放了。于是所返回的char*什么
    也沒有。而對于第二個函數,是從堆上分配內存,所以哪怕是程序退出時,也不釋放,所
    以第二個函數的返回的內存沒有問題,可以被使用。但一定要調用free釋放,不然就是Mem
    ory Leak!
    ?
    在堆上分配內存很容易造成內存泄漏,這是C/C++的最大的“克星”,如果你的程序要穩定
    ,那么就不要出現Memory Leak。所以,我還是要在這里千叮嚀萬囑付,在使用malloc系統
    蛑齦叮謔褂胢alloc系統
    函數(包括calloc,realloc)時千萬要小心。
    ?
    記得有一個UNIX上的服務應用程序,大約有幾百的C文件編譯而成,運行測試良好,等使用
    時,每隔三個月系統就是down一次,搞得許多人焦頭爛額,查不出問題所在。只好,每隔
    兩個月人工手動重啟系統一次。出現這種問題就是Memery Leak在做怪了,在C/C++中這種
    問題總是會發生,所以你一定要小心。一個Rational的檢測工作——Purify,可以幫你測
    試你的程序有沒有內存泄漏。
    ?
    我保證,做過許多C/C++的工程的程序員,都會對malloc或是new有些感冒。當你什么時候
    在使用malloc和new時,有一種輕度的緊張和惶恐的感覺時,你就具備了這方面的修養了。
    ?
    對于malloc和free的操作有以下規則:
    ?
    1) 配對使用,有一個malloc,就應該有一個free。(C++中對應為new和delete)
    2) 盡量在同一層上使用,不要像上面那種,malloc在函數中,而free在函數外。最好在同
    一調用層上使用這兩個函數。
    3) malloc分配的內存一定要初始化。free后的指針一定要設置為NULL。
    ?
    注:雖然現在的操作系統(如:UNIX和Win2k/NT)都有進程內存跟蹤機制,也就是如果你
    有沒有釋放的內存,操作系統會幫你釋放。但操作系統依然不會釋放你程序中所有產生了M
    emory Leak的內存,所以,最好還是你自己來做這個工作。(有的時候不知不覺就出現Mem
    ory Leak了,而且在幾百萬行的代碼中找無異于海底撈針,Rational有一個工具叫Purify
    蛐械拇脛姓椅摶煊諍5桌陶耄琑ational有一個工具叫Purify
    ,可能很好的幫你檢查程序中的Memory Leak)
    ?
    ?
    ?
    9、變量的初始化
    ————————
    接上一條,變量一定要被初始化再使用。C/C++編譯器在這個方面不會像JAVA一樣幫你初始
    化,這一切都需要你自己來,如果你使用了沒有初始化的變量,結果未知。好的程序員從
    來都會在使用變量前初始化變量的。如:
    ?
    ??? 1) 對malloc分配的內存進行memset清零操作。(可以使用calloc分配一塊全零的內存

    ??? 2) 對一些棧上分配的struct或數組進行初始化。(最好也是清零)
    ?
    不過話又說回來了,初始化也會造成系統運行時間有一定的開銷,所以,也不要對所有的
    變量做初始化,這個也沒有意義。好的程序員知道哪些變量需要初始化,哪些則不需要。
    如:以下這種情況,則不需要。
    ?
    ??????? char *pstr;? /* 一個字符串 */
    ??????? pstr = ( char* ) malloc( 50 );
    ??????? if ( pstr == NULL ) exit(0);
    ??????? strcpy( pstr, "Hello Wrold" );
    ??????? strcpy( pstr, "Hello Wrold" );
    ?
    但如果是下面一種情況,最好進行內存初始化。(指針是一個危險的東西,一定要初始化

    ?
    ??????? char **pstr;? /* 一個字符串數組 */
    ??????? pstr = ( char** ) malloc( 50 );
    ??????? if ( pstr == NULL ) exit(0);
    ?
    ??????? /* 讓數組中的指針都指向NULL */
    ??????? memset( pstr, 0, 50*sizeof(char*) );
    ?
    而對于全局變量,和靜態變量,一定要聲明時就初始化。因為你不知道它第一次會在哪里
    被使用。所以使用前初始這些變量是比較不現實的,一定要在聲明時就初始化它們。如:
    ?
    ??? Links *plnk = NULL;? /* 對于全局變量plnk初始化為NULL */
    ?
    ?
    ?
    ?
    ?
    10、h和c文件的使用
    —————————
    —————————
    H文件和C文件怎么用呢?一般來說,H文件中是declare(聲明),C文件中是define(定義
    )。因為C文件要編譯成庫文件(Windows下是.obj/.lib,UNIX下是.o/.a),如果別人要
    使用你的函數,那么就要引用你的H文件,所以,H文件中一般是變量、宏定義、枚舉、結
    構和函數接口的聲明,就像一個接口說明文件一樣。而C文件則是實現細節。
    ?
    H文件和C文件最大的用處就是聲明和實現分開。這個特性應該是公認的了,但我仍然看到
    有些人喜歡把函數寫在H文件中,這種習慣很不好。(如果是C++話,對于其模板函數,在V
    C中只有把實現和聲明都寫在一個文件中,因為VC不支持export關鍵字)。而且,如果在H
    文件中寫上函數的實現,你還得在makefile中把頭文件的依賴關系也加上去,這個就會讓
    你的makefile很不規范。
    ?
    最后,有一個最需要注意的地方就是:帶初始化的全局變量不要放在H文件中!
    ?
    例如有一個處理錯誤信息的結構:
    ?
    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",
    ??????? ......
    ??????? ......
    ??? };
    ?
    請不要把這個東西放在頭文件中,因為如果你的這個頭文件被5個函數庫(.lib或是.a)所
    用到,于是他就被鏈接在這5個.lib或.a中,而如果你的一個程序用到了這5個函數庫中的
    函數,并且這些函數都用到了這個出錯信息數組。那么這份信息將有5個副本存在于你的執
    行文件中。如果你的這個errmsg很大的話,而且你用到的函數庫更多的話,你的執行文件
    也會變得很大。
    ?
    正確的寫法應該把它寫到C文件中,然后在各個需要用到errmsg的C文件頭上加上 extern
    char* errmsg[]; 的外部聲明,讓編譯器在鏈接時才去管他,這樣一來,就只會有一個err
    msg存在于執行文件中,而且,這樣做很利于封裝。
    ?
    我曾遇到過的最瘋狂的事,就是在我的目標文件中,這個errmsg一共有112個副本,執行文
    件有8M左右。當我把errmsg放到C文件中,并為一千多個C文件加上了extern的聲明后,所
    有的函數庫文件尺寸都下降了20%左右,而我的執行文件只有5M了。一下子少了3M啊。
    ?
    [ 備注 ]
    —————
    有朋友對我說,這個只是一個特例,因為,如果errmsg在執行文件中存在多個副本時,可
    以加快程序運行速度,理由是errmsg的多個復本會讓系統的內存換頁降低,達到效率提升
    。像我們這里所說的errmsg只有一份,當某函數要用errmsg時,如果內存隔得比較遠,會
    產生換頁,反而效率不高。
    ?
    ?
    生副本導致執行文件尺寸變大,不僅增加了系統裝載時間,也會讓一個程序在內存中占更
    多的頁面。而對于errmsg這樣數據,一般來說,在系統運行時不會經常用到,所以還是產
    生的內存換頁也就不算頻繁。權衡之下,還是只有一份errmsg的效率高。即便是像logmsg
    這樣頻繁使用的的數據,操作系統的內存調度算法會讓這樣的頻繁使用的頁面常駐于內存
    ,所以也就不會出現內存換頁問題了。
    11、出錯信息的處理
    —————————
    你會處理出錯信息嗎?哦,它并不是簡單的輸出。看下面的示例:
    ?
    ??? if ( p == NULL ){
    ??????? printf ( "ERR: The pointer is NULL\n" );
    ??? }
    ?
    告別學生時代的編程吧。這種編程很不利于維護和管理,出錯信息或是提示信息,應該統
    一處理,而不是像上面這樣,寫成一個“硬編碼”。第10條對這方面的處理做了一部分說
    明。如果要管理錯誤信息,那就要有以下的處理:
    ?
    ??? /* 聲明出錯代碼 */
    ??? #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?? */
    ?
    ??? /* 聲明出錯信息 */
    ??? 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",
    ??? };
    ?
    ??? /* 聲明錯誤代碼全局變量 */
    ??? long errno = 0;
    ?
    ??? /* 打印出錯信息函數 */
    ??? void perror( char* info)
    ??? {
    ??????? if ( info ){
    ??????????? printf("%s: %s\n", info, errmsg[errno] );
    ??????????? return;
    ??????? }
    ?
    ??????? printf("Error: %s\n", errmsg[errno] );
    ??? }
    ?
    這個基本上是ANSI的錯誤處理實現細節了,于是當你程序中有錯誤時你就可以這樣處理:
    ?
    ??? bool CheckPermission( char* userName )
    ??? {
    ??????? if ( strcpy(userName, "root") != 0 ){
    ??????????? errno = ERR_PERMISSION_DENIED;
    ??????????? return (FALSE);
    ??????? }
    ?
    ??????? ...
    ??? }
    ?
    ??? main()
    ??? {
    ??????? ...
    ??????? if (! CheckPermission( username ) ){
    ??????????? perror("main()");
    ??????? }
    ??????? ...
    ??? }
    ?
    一個即有共性,也有個性的錯誤信息處理,這樣做有利同種錯誤出一樣的信息,統一用戶
    界面,而不會因為文件打開失敗,A程序員出一個信息,B程序員又出一個信息。而且這樣
    做,非常容易維護。代碼也易讀。
    ?
    當然,物極必反,也沒有必要把所有的輸出都放到errmsg中,抽取比較重要的出錯信息或
    是提示信息是其關鍵,但即使這樣,這也包括了大多數的信息。
    ?
    ?
    ?
    ?
    ?
    12、常用函數和循環語句中的被計算量
    —————————————————
    看一下下面這個例子:
    ?
    ??? for( i=0; i<1000; i++ ){
    ??????? GetLocalHostName( hostname );
    ??????? ...
    ??? }
    ?
    GetLocalHostName的意思是取得當前計算機名,在循環體中,它會被調用1000次啊。這是
    多么的沒有效率的事啊。應該把這個函數拿到循環體外,這樣只調用一次,效率得到了很
    大的提高。雖然,我們的編譯器會進行優化,會把循環體內的不變的東西拿到循環外面,
    但是,你相信所有編譯器會知道哪些是不變的嗎?我覺得編譯器不可靠。最好還是自己動
    手吧。
    ?
    同樣,對于常用函數中的不變量,如:
    ?
    GetLocalHostName(char* name)
    {
    {
    ??? char funcName[] = "GetLocalHostName";
    ?
    ??? sys_log( "%s begin......", funcName );
    ??? ...
    ??? sys_log( "%s end......", funcName );
    }
    ?
    如果這是一個經常調用的函數,每次調用時都要對funcName進行分配內存,這個開銷很大
    啊。把這個變量聲明成static吧,當函數再次被調用時,就會省去了分配內存的開銷,執
    行效率也很好。
    ?
    ?
    ?
    ?
    13、函數名和變量名的命名
    ————————————
    我看到許多程序對變量名和函數名的取名很草率,特別是變量名,什么a,b,c,aa,bb,cc,
    還有什么flag1,flag2, cnt1, cnt2,這同樣是一種沒有“修養”的行為。即便加上好的注
    釋。好的變量名或是函數名,我認為應該有以下的規則:
    ?
    ??? 1) 直觀并且可以拼讀,可望文知意,不必“解碼”。
    ??? 2) 名字的長度應該即要最短的長度,也要能最大限度的表達其含義。
    ??? 3) 不要全部大寫,也不要全部小寫,應該大小寫都有,如:GetLocalHostName 或是
    UserAccount。
    ??? 4) 可以簡寫,但簡寫得要讓人明白,如:ErrorCode -> ErrCode,
    ServerListener -> ServLisner,UserAccount -> UsrAcct 等。
    ??? 5) 為了避免全局函數和變量名字沖突,可以加上一些前綴,一般以模塊簡稱做為前綴

    ??? 6) 全局變量統一加一個前綴或是后綴,讓人一看到這個變量就知道是全局的。
    ??? 7) 用匈牙利命名法命名函數參數,局部變量。但還是要堅持“望文生意”的原則。
    ??? 8) 與標準庫(如:STL)或開發庫(如:MFC)的命名風格保持一致。
    ?
    ?
    ?
    ?
    14、函數的傳值和傳指針
    ————————————
    向函數傳參數時,一般而言,傳入非const的指針時,就表示,在函數中要修改這個指針把
    指內存中的數據。如果是傳值,那么無論在函數內部怎么修改這個值,也影響不到傳過來
    的值,因為傳值是只內存拷貝。
    ?
    什么?你說這個特性你明白了,好吧,讓我們看看下面的這個例程:
    ?
    void
    void
    GetVersion(char* pStr)
    {
    ??? pStr = malloc(10);
    ??? strcpy ( pStr, "2.0" );
    }
    ?
    main()
    {
    ??? char* ver = NULL;
    ??? GetVersion ( ver );
    ??? ...
    ??? ...
    ??? free ( ver );
    }
    ?
    我保證,類似這樣的問題是一個新手最容易犯的錯誤。程序中妄圖通過函數GetVersion給
    指針ver分配空間,但這種方法根本沒有什么作用,原因就是——這是傳值,不是傳指針。
    你或許會和我爭論,我分明傳的時指針啊?再仔細看看,其實,你傳的是指針其實是在傳
    值。
    ?
    ?
    ?
    ?
    15、修改別人程序的修養
    ———————————
    ?
    當你維護別人的程序時,請不要非常主觀臆斷的把已有的程序刪除或是修改。我經常看到
    有的程序員直接在別人的程序上修改表達式或是語句。修改別人的程序時,請不要刪除別
    人的程序,如果你覺得別人的程序有所不妥,請注釋掉,然后添加自己的處理程序,必竟
    ,你不可能100%的知道別人的意圖,所以為了可以恢復,請不依賴于CVS或是SourceSafe這
    種版本控制軟件,還是要在源碼上給別人看到你修改程序的意圖和步驟。這是程序維護時
    ,一個有修養的程序員所應該做的。
    ?
    如下所示,這就是一種比較好的修改方法:
    ?
    ??? /*
    ???? * ----- 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 );
    ??? /* ---------------------------------------- */
    ??? ...
    ?
    當然,這種方法是在軟件維護時使用的,這樣的方法,可以讓再維護的人很容易知道以前
    的代碼更改的動作和意圖,而且這也是對原作者的一種尊敬。
    ?
    以“注釋 — 添加”方式修改別人的程序,要好于直接刪除別人的程序。
    --
    16、把相同或近乎相同的代碼形成函數和宏
    —————————————————————
    ?
    有人說,最好的程序員,就是最喜歡“偷懶”的程序,其中不無道理。
    ?
    如果你有一些程序的代碼片段很相似,或直接就是一樣的,請把他們放在一個函數中。而
    如果這段代碼不多,而且會被經常使用,你還想避免函數調用的開銷,那么就把他寫成宏
    吧。
    ?
    千萬不要讓同一份代碼或是功能相似的代碼在多個地方存在,不然如果功能一變,你就要
    修改好幾處地方,這種會給維護帶來巨大的麻煩,所以,做到“一改百改”,還是要形成
    函數或是宏。
    ?
    ?
    ?
    17、表達式中的括號
    17、表達式中的括號
    —————————
    ?
    如果一個比較復雜的表達式中,你并不是很清楚各個操作符的憂先級,即使是你很清楚優
    先級,也請加上括號,不然,別人或是自己下一次讀程序時,一不小心就看走眼理解錯了
    ,為了避免這種“誤解”,還有讓自己的程序更為清淅,還是加上括號吧。
    ?
    比如,對一個結構的成員取地址:
    ?
    ??? GetUserAge( &( UserInfo->age ) );
    ?
    雖然,&UserInfo->age中,->操作符的優先級最高,但加上一個括號,會讓人一眼就看明
    白你的代碼是什么意思。
    ?
    再比如,一個很長的條件判斷:
    ?
    if ( ( ch[0] >= '0' || ch[0] <= '9' ) &&
    ???? ( ch[1] >= 'a' || ch[1] <= 'z' ) &&
    ???? ( ch[2] >= 'A' || ch[2] <= 'Z' )??? )
    ?
    括號,再加上空格和換行,你的代碼是不是很容易讀懂了?
    ?
    ?
    ?
    18、函數參數中的const
    ———————————
    ?
    對于一些函數中的指針參數,如果在函數中只讀,請將其用const修飾,這樣,別人一讀到
    你的函數接口時,就會知道你的意圖是這個參數是[in],如果沒有const時,參數表示[in/
    out],注意函數接口中的const使用,利于程序的維護和避免犯一些錯誤。
    ?
    雖然,const修飾的指針,如:const char* p,在C中一點用也沒有,因為不管你的聲明是
    不是const,指針的內容照樣能改,因為編譯器會強制轉換,但是加上這樣一個說明,有利
    于程序的閱讀和編譯。因為在C中,修改一個const指針所指向的內存時,會報一個Warning
    。這會引起程序員的注意。
    ?
    C++中對const定義的就很嚴格了,所以C++中要多多的使用const,const的成員函數,cons
    t的變量,這樣會對讓你的代碼和你的程序更加完整和易讀。(關于C++的const我就不多說
    了)
    ?
    ?
    ?
    19、函數的參數個數(多了請用結構)
    —————————————————
    ?
    函數的參數個數最好不要太多,一般來說6個左右就可以了,眾多的函數參數會讓讀代碼的
    人一眼看上去就很頭昏,而且也不利于維護。如果參數眾多,還請使用結構來傳遞參數。
    這樣做有利于數據的封裝和程序的簡潔性。
    ?
    也利于使用函數的人,因為如果你的函數個數很多,比如12個,調用者很容易搞錯參數的
    順序和個數,而使用結構struct來傳遞參數,就可以不管參數的順序。
    ?
    而且,函數很容易被修改,如果需要給函數增加參數,不需要更改函數接口,只需更改結
    構體和函數內部處理,而對于調用函數的程序來說,這個動作是透明的。
    ?
    ?
    ?
    ?
    20、函數的返回類型,不要省略
    ——————————————
    ?
    我看到很多程序寫函數時,在函數的返回類型方面不太注意。如果一個函數沒有返回值,
    也請在函數前面加上void的修飾。而有的程序員偷懶,在返回int的函數則什么不修飾(因
    為如果不修飾,則默認返回int),這種習慣很不好,還是為了原代碼的易讀性,加上int
    吧。
    ?
    所以函數的返回值類型,請不要省略。
    ?
    ?
    另外,對于void的函數,我們往往會忘了return,由于某些C/C++的編譯器比較敏感,會報
    一些警告,所以即使是void的函數,我們在內部最好也要加上return的語句,這有助于代
    碼的編譯。
    ?
    ?
    ?
    ?
    21、goto語句的使用
    —————————
    ?
    N年前,軟件開發的一代宗師——迪杰斯特拉(Dijkstra)說過:“goto statment is
    harmful !!”,并建議取消goto語句。因為goto語句不利于程序代碼的維護性。
    ?
    這里我也強烈建議不要使用goto語句,除非下面的這種情況:
    ?
    ?
    ??? #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語句會讓你的程序更易讀,更容易維護。(在用嵌C來對數據庫
    設置游標操作時,或是對數據庫建立鏈接時,也會遇到這種結構)
    ?
    ?
    ?
    ?
    22、宏的使用
    ——————
    ?
    很多程序員不知道C中的“宏”到底是什么意思?特別是當宏有參數的時候,經常把宏和函
    數混淆。我想在這里我還是先講講“宏”,宏只是一種定義,他定義了一個語句塊,當程
    序編譯時,編譯器首先要執行一個“替換”源程序的動作,把宏引用的地方替換成宏定義
    的語句塊,就像文本文件替換一樣。這個動作術語叫“宏的展開”
    ?
    使用宏是比較“危險”的,因為你不知道宏展開后會是什么一個樣子。例如下面這個宏:
    ?
    ?
    ??? #define? MAX(a, b)???? a>b?a:b
    ?
    當我們這樣使用宏時,沒有什么問題: MAX( num1, num2 ); 因為宏展開后變成
    num1>num2?num1:num2;。 但是,如果是這樣調用的,MAX( 17+32, 25+21 ); 呢,編譯時
    出現錯誤,原因是,宏展開后變成:17+32>25+21?17+32:25+21,哇,這是什么啊?
    ?
    所以,宏在使用時,參數一定要加上括號,上述的那個例子改成如下所示就能解決問題了

    ?
    ??? #define? MAX( (a), (b) )???? (a)>(b)?(a):(b)
    ?
    即使是這樣,也不這個宏也還是有Bug,因為如果我這樣調用 MAX(i++, j++); , 經過這
    個宏以后,i和j都被累加了兩次,這絕不是我們想要的。
    ?
    所以,在宏的使用上還是要謹慎考慮,因為宏展開是的結果是很難讓人預料的。而且雖然
    ,宏的執行很快(因為沒有函數調用的開銷),但宏會讓源代碼澎漲,使目標文件尺寸變
    大,(如:一個50行的宏,程序中有1000個地方用到,宏展開后會很不得了),相反不能
    讓程序執行得更快(因為執行文件變大,運行時系統換頁頻繁)。
    ?
    因此,在決定是用函數,還是用宏時得要小心。
    --
    ?
    。???????????????? 開始使勁
    ?
    ??? #define? MAX( (a), (b) )???? (a)>(b)?(a):(b)
    ?
    即使是這樣,也不這個宏也還是有Bug,因為如果我這樣調用 MAX(i++, j++); , 經過這
    個宏以后,i和j都被累加了兩次,這絕不是我們想要的。
    ?
    所以,在宏的使用上還是要謹慎考慮,因為宏展開是的結果是很難讓人預料的。而且雖然
    ,宏的執行很快(因為沒有函數調用的開銷),但宏會讓源代碼澎漲,使目標文件尺寸變
    大,(如:一個50行的宏,程序中有1000個地方用到,宏展開后會很不得了),相反不能
    讓程序執行得更快(因為執行文件變大,運行時系統換頁頻繁)。
    ?
    因此,在決定是用函數,還是用宏時得要小心。
    --
    23、static的使用
    ————————
    static關鍵字,表示了“靜態”,一般來說,他會被經常用于變量和函數。一個static的
    變量,其實就是全局變量,只不過他是有作用域的全局變量。比如一個函數中的static變
    量:
    ?
    char*
    getConsumerName()
    {
    ??? static int cnt = 0;
    ?
    ??? ....
    ??? cnt++;
    ??? ....
    }
    ?
    ?
    cnt變量的值會跟隨著函數的調用次而遞增,函數退出后,cnt的值還存在,只是cnt只能在
    函數中才能被訪問。而cnt的內存也只會在函數第一次被調用時才會被分配和初始化,以后
    每次進入函數,都不為static分配了,而直接使用上一次的值。
    ?
    對于一些被經常調用的函數內的常量,最好也聲明成static(參見第12條)
    ?
    但static的最多的用處卻不在這里,其最大的作用的控制訪問,在C中如果一個函數或是一
    個全局變量被聲明為static,那么,這個函數和這個全局變量,將只能在這個C文件中被訪
    問,如果別的C文件中調用這個C文件中的函數,或是使用其中的全局(用extern關鍵字)
    ,將會發生鏈接時錯誤。這個特性可以用于數據和程序保密。
    ?
    ?
    ?
    24、函數中的代碼尺寸
    ——————————
    一個函數完成一個具體的功能,一般來說,一個函數中的代碼最好不要超過600行左右,越
    少越好,最好的函數一般在100行以內,300行左右的孫函數就差不多了。有證據表明,一
    個函數中的代碼如果超過500行,就會有和別的函數相同或是相近的代碼,也就是說,就可
    以再寫另一個函數。
    ?
    另外,函數一般是完成一個特定的功能,千萬忌諱在一個函數中做許多件不同的事。函數
    的功能越單一越好,一方面有利于函數的易讀性,另一方面更有利于代碼的維護和重用,
    功能越單一表示這個函數就越可能給更多的程序提供服務,也就是說共性就越多。
    ?
    雖然函數的調用會有一定的開銷,但比起軟件后期維護來說,增加一些運行時的開銷而換
    來更好的可維護性和代碼重用性,是很值得的一件事。
    ?
    ?
    25、typedef的使用
    —————————
    ?
    typedef是一個給類型起別名的關鍵字。不要小看了它,它對于你代碼的維護會有很好的作
    用。比如C中沒有bool,于是在一個軟件中,一些程序員使用int,一些程序員使用short,
    會比較混亂,最好就是用一個typedef來定義,如:
    ?
    ??? typedef char bool;
    ?
    一般來說,一個C的工程中一定要做一些這方面的工作,因為你會涉及到跨平臺,不同的平
    臺會有不同的字長,所以利用預編譯和typedef可以讓你最有效的維護你的代碼,如下所示

    ?
    ??? #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的其它規范是,在結構和函數指針時,也最好用typedef,這也有利于程序的
    易讀和可維護性。如:

    ??? 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;
    ?
    于是,當我們用其定義變量時,會顯得十分易讀。如:
    ?
    ??? HostInfo* phinfo;
    ??? RsrcReqHandler* pRsrcHand;
    ?
    這種方式的易讀性,在函數的參數中十分明顯。
    ?
    關鍵是在程序種使用typedef后,幾乎所有的程序中的類型聲明都顯得那么簡潔和清淅,而
    且易于維護,這才是typedef的關鍵。
    ?
    ?
    ?
    26、為常量聲明宏
    ————————
    最好不要在程序中出現數字式的“硬編碼”,如:
    ?
    ??? int user[120];
    ?
    為這個120聲明一個宏吧。為所有出現在程序中的這樣的常量都聲明一個
    宏吧。比如TimeOut的時間,最大的用戶數量,還有其它,只要是常量就應該聲明成宏。如
    果,突然在程序中出現下面一段代碼,
    ?
    ??? for ( i=0; i<120; i++){
    ??????? ....
    ??? }
    ?
    120是什么?為什么會是120?這種“硬編碼”不僅讓程序很讀,而且也讓程序很不好維護
    ,如果要改變這個數字,得同時對所有程序中這個120都要做修改,這對修改程序的人來說
    是一個很大的痛苦。所以還是把常量聲明成宏,這樣,一改百改,而且也很利于程序閱讀

    ?
    ??? #define MAX_USR_CNT 120
    ?
    ??? for ( i=0; i<MAX_USER_CNT; i++){
    ??????? ....
    ??? }
    ?
    這樣就很容易了解這段程序的意圖了。
    ?
    有的程序員喜歡為這種變量聲明全局變量,其實,全局變量應該盡量的少用,全局變量不
    利于封裝,也不利于維護,而且對程序執行空間有一定的開銷,一不小心就造成系統換頁
    ,造成程序執行速度效率等問題。所以聲明成宏,即可以免去全局變量的開銷,也會有速
    ,造成程序執行速度效率等問題。所以聲明成宏,即可以免去全局變量的開銷,也會有速
    度上的優勢。
    ?
    ?
    27、不要為宏定義加分號
    ———————————
    ?
    有許多程序員不知道在宏定義時是否要加分號,有時,他們以為宏是一條語句,應該要加
    分號,這就錯了。當你知道了宏的原理,你會贊同我為會么不要為宏定義加分號的。看一
    個例子:
    ?
    ??? #define MAXNUM 1024;
    ?
    這是一個有分號的宏,如果我們這樣使用:
    ?
    ??? half = MAXNUM/2;
    ?
    ??? if ( num < MAXNUM )
    ?
    等等,都會造成程序的編譯錯誤,因為,當宏展開后,他會是這個樣子的:
    ?
    ??? half = 1024;/2;
    ?
    ?
    ??? if ( num < 1024; )
    ?
    是的,分號也被展進去了,所以造成了程序的錯誤。請相信我,有時候,一個分號會讓你
    的程序出現成百個錯誤。所以還是不要為宏加最后一個分號,哪怕是這樣:
    ?
    ??? #define LINE??? "================================="
    ?
    ??? #define PRINT_LINE? printf(LINE)
    ?
    ??? #define PRINT_NLINE(n)? while ( n-- >0 ) { PRINT_LINE; }
    ?
    都不要在最后加上分號,當我們在程序中使用時,為之加上分號,
    ?
    ??? main()
    ??? {
    ??????? char *p = LINE;
    ??????? PRINT_LINE;
    ??? }
    ?
    這一點非常符合習慣,而且,如果忘加了分號,編譯器給出的錯誤提示,也會讓我們很容
    易看懂的。
    --
    ?????????????????? 開始使勁
    ??? #define PRINT_NLINE(n)? while ( n-- >0 ) { PRINT_LINE; }
    ?
    都不要在最后加上分號,當我們在程序中使用時,為之加上分號,
    ?
    ??? main()
    ??? {
    ??????? char *p = LINE;
    ??????? PRINT_LINE;
    ??? }
    ?
    這一點非常符合習慣,而且,如果忘加了分號,編譯器給出的錯誤提示,也會讓我們很容
    易看懂的。
    --
    28、||和&&的語句執行順序
    ————————————
    條件語句中的這兩個“與”和“或”操作符一定要小心,它們的表現可能和你想像的不一
    樣,這里條件語句中的有些行為需要和說一下:
    ?
    ??? express1 || express2
    ?
    ??? 先執行表達式express1如果為“真”,express2將不被執行,express2僅在express1
    為“假”時才被執行。因為第一個表達式為真了,整個表達式都為真,所以沒有必要再去
    執行第二個表達式了。
    ?
    ??? express1 && express2
    ?
    ??? 先執行表達式express1如果為“假”,express2將不被執行,express2僅在express1
    為“真”時才被執行。因為第一個表達式為假了,整個表達式都為假了,所以沒有必要再
    去執行第二個表達式了。

    ?
    于是,他并不是你所想像的所有的表達式都會去執行,這點一定要明白,不然你的程序會
    出現一些莫明的運行時錯誤。
    ?
    例如,下面的程序:
    ?
    ?
    ??? 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 ,向文件中寫一條出錯信息,為了方便,把兩個條件判斷
    寫在一起,于是,如果sum<=100時,打開文件的操作將不會做,最后,fprintf和fclose就
    會發現未知的結果。
    ?
    ?
    再比如,如果我想判斷一個字符是不是有內容,我得判斷這個字符串指針是不為空(NULL
    )并且其內容不能為空(Empty),一個是空指針,一個是空內容。我也許會這樣寫:
    if ( ( p != NULL ) && ( strlen(p) != 0 ))
    于是,如果p為NULL,那么strlen(p)就不會被執行,于是,strlen也就不會因為一個空指
    針而“非法操作”或是一個“Core Dump”了。
    記住一點,條件語句中,并非所有的語句都會執行,當你的條件語句非常多時,這點要尤
    其注意。
    ?
    29、盡量用for而不是while做循環
    ———————————————
    基本上來說,for可以完成while的功能,我是建議盡量使用for語句,而不要使用while語
    句,特別是當循環體很大時,for的優點一下就體現出來了。
    因為在for中,循環的初始、結束條件、循環的推進,都在一起,一眼看上去就知道這是一
    個什么樣的循環。剛出學校的程序一般對于鏈接喜歡這樣來:
    p = pHead;
    p = pHead;
    while ( p ){
    ...
    ...
    p = p->next;
    }
    當while的語句塊變大后,你的程序將很難讀,用for就好得多:
    for ( p=pHead; p; p=p->next ){
    ..
    }
    一眼就知道這個循環的開始條件,結束條件,和循環的推進。大約就能明白這個循環要做
    個什么事?而且,程序維護進來很容易,不必像while一樣,在一個編輯器中上上下下的搗
    騰。
    ?
    30、請sizeof類型而不是變量
    —————————————

    許多程序員在使用sizeof中,喜歡sizeof變量名,例如:
    int score[100];
    char filename[20];
    struct UserInfo usr[100];
    在sizeof這三個的變量名時,都會返回正確的結果,于是許多程序員就開始sizeof變量名
    。這個習慣很雖然沒有什么不好,但我還是建議sizeof類型。
    我看到過這個的程序:
    pScore = (int*) malloc( SUBJECT_CNT );
    memset( pScore, 0, sizeof(pScore) );
    ...
    此時,sizeof(pScore)返回的就是4(指針的長度),不會是整個數組,于是,memset就不
    能對這塊內存進行初始化。為了程序的易讀和易維護,我強烈建議使用類型而不是變量,
    如:
    對于score: sizeof(int) * 100 /* 100個int */
    對于filename: sizeof(char) * 20 /* 20個char */
    對于usr: sizeof(struct UserInfo) * 100 /* 100個UserInfo */
    這樣的代碼是不是很易讀?一眼看上去就知道什么意思了。

    另外一點,sizeof一般用于分配內存,這個特性特別在多維數組時,就能體現出其優點了
    。如,給一個字符串數組分配內存,
    /*
    * 分配一個有20個字符串,
    * 每個字符串長100的內存
    */
    char* *p;
    /*
    * 錯誤的分配方法
    */
    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) );
    }
    (注:上述語句被注釋掉的是原來的,是錯誤的,由dasherest朋友指正,謝謝)
    為了代碼的易讀,省去了一些判斷,請注意這兩種分配的方法,有本質上的差別。
    ?
    31、不要忽略Warning
    ——————————
    對于一些編譯時的警告信息,請不要忽視它們。雖然,這些Warning不會妨礙目標代碼的生
    成,但這并不意味著你的程序就是好的。必竟,并不是編譯成功的程序才是正確的,編譯
    成功只是萬里長征的第一步,后面還有大風大浪在等著你。從編譯程序開始,不但要改正
    每個error,還要修正每個warning。這是一個有修養的程序員該做的事。
    一般來說,一面的一些警告信息是常見的:

    1)聲明了未使用的變量。(雖然編譯器不會編譯這種變量,但還是把它從源程序中注
    釋或是刪除吧)
    2)使用了隱晦聲明的函數。(也許這個函數在別的C文件中,編譯時會出現這種警告
    ,你應該這使用之前使用extern關鍵字聲明這個函數)
    3)沒有轉換一個指針。(例如malloc返回的指針是void的,你沒有把之轉成你實際類
    型而報警,還是手動的在之前明顯的轉換一下吧)
    4)類型向下轉換。(例如:float f = 2.0; 這種語句是會報警告的,編譯會告訴你
    正試圖把一個double轉成float,你正在閹割一個變量,你真的要這樣做嗎?還是在2.0后
    面加個f吧,不然,2.0就是一個double,而不是float了)
    不管怎么說,編譯器的Warning不要小視,最好不要忽略,一個程序都做得出來,何況幾個
    小小的Warning呢?
    ?
    32、書寫Debug版和Release版的程序
    ————————————————
    程序在開發過程中必然有許多程序員加的調試信息。我見過許多項目組,當程序開發結束
    時,發動群眾刪除程序中的調試信息,何必呢?為什么不像VC++那樣建立兩個版本的目標
    代碼?一個是debug版本的,一個是Release版的。那些調試信息是那么的寶貴,在日后的
    維護過程中也是很寶貴的東西,怎么能說刪除就刪除呢?

    利用預編譯技術吧,如下所示聲明調試函數:
    #ifdef DEBUG
    void TRACE(char* fmt, ...)
    {
    ......
    }
    #else
    #define TRACE(char* fmt, ...)
    #endif
    于是,讓所有的程序都用TRACE輸出調試信息,只需要在在編譯時加上一個參數“-DDEBUG
    ”,如:
    cc -DDEBUG -o target target.c
    于是,預編譯器發現DEBUG變量被定義了,就會使用TRACE函數。而如果要發布給用戶了,
    那么只需要把取消“-DDEBUG”的參數,于是所有用到TRACE宏,這個宏什么都沒有,所以
    源程序中的所有TRACE語言全部被替換成了空。一舉兩得,一箭雙雕,何樂而不為呢?
    順便提一下,兩個很有用的系統宏,一個是“__FILE__”,一個是“__LINE__”,分別表
    示,所在的源文件和行號,當你調試信息或是輸出錯誤時,可以使用這兩個宏,讓你一眼
    就能看出你的錯誤,出現在哪個文件的第幾行中。這對于用C/C++做的大工程非常的管用。
    綜上所述32條,都是為了三大目的——
    1、?程序代碼的易讀性。
    2、程序代碼的可維護性,
    3、程序代碼的穩定可靠性
    的細小的問題,編程高手不僅技術要強,基礎要好,而且最重要的是要有“修養”!
    ?
    軟件的維護有大量的工作量花在代碼的維護上,軟件的Upgrade,也有大量的工作花在代碼的組織上,所以好的代碼,清淅的,易讀的代碼,將給大大減少軟件的維護和升級成本。

    posted on 2006-12-08 21:16 蘆葦 閱讀(3112) 評論(1)  編輯  收藏 所屬分類: 其他

    Feedback

    # 垃圾書 2007-10-13 13:52 胡三


    奧林匹克運動的創始人顧拜旦簡介
    來源:新華網 作者:olympics 發布時間:2007-08-11


    奧林匹克運動的創始人顧拜旦簡介:

      勒巴龍·皮埃爾·德·顧拜旦 (Lebaron Pierre De Coubertin)1863年1月1日出生于法國巴黎的一個貴族家庭,在家排行第4,也是最小的孩子。1471年,他的一位先祖得到了國王路易十一授予的貴族稱號。他的父親夏爾·德·顧拜旦是個頗有名氣的水彩畫家。他的母親瑪麗也是貴族后裔。顧拜旦從他父母處繼承了大筆的遺產。1896年至1925年任國際奧委會主席,他終生倡導奧林匹克精神,被譽為“現代奧林匹克之父”。

      顧拜旦的童年是在諾曼底度過的。當時的顧拜旦非常喜歡拳擊、賽艇、擊劍和騎馬等項體育活動,并喜歡畫畫,會彈鋼琴。1875年至1881年,古代奧運會的遺址不斷被發掘出來,這引起顧拜旦極大的興趣。

      1880年,顧拜旦進入法國著名的圣西爾軍校。但不久就退學,隨后進入法國巴黎政治學院。后又入法國巴黎大學法學院就讀,獲得了文學、科學和法學三個學位。青年時代的顧拜旦志在教育和歷史,在普法戰爭中戰敗而萌生了教育救國和體育救國的思想。1983年起進行比較教育學的學術研究。1888年,顧拜旦出任法國學校體育訓練籌備委員會秘書長,并發起成立了第一個“全法學校體育協會”,設立了“皮埃爾·德·顧拜旦獎”,以表彰最優秀的運動員。1889年,召開推廣在教育中設立身體練習課程代表大會,他擔任大會秘書長。

      1890年,他訪問了奧林匹克運動的發源地——希臘的奧林匹亞,使他萌生應以古代奧林匹克精神來推進國際體育運動的想法,以創辦現代奧運來弘揚奧林匹克精神。一種舉辦世界性的奧林匹克運動會的設想使他開始積極投入到創辦現代奧運會的工作之中。1891年,顧拜旦出任法國田徑協會秘書長。

      1892年12月25日,法國田協在巴黎索邦大學舉行成立5周年紀念大會,顧拜旦首次在大會演講中發表了“復興奧林匹克運動”的著名演說,正式提出恢復和創辦現代奧運會的想法。1893年,在顧拜旦的推動下,在巴黎召開了“恢復奧林匹克運動代表大會”。來自12個歐美國家的代表們一致通過了恢復奧林匹克運動的憲章,確定了現代奧運會的宗旨。這次會議還決定于1896年4月在奧林匹克運動發詳地希臘舉行第一屆現代奧運會,以后則按照古奧運會的傳統,每4年舉行一次。1894年6月23日,國際奧林匹克委員會成立,當時著名希臘詩人維凱拉斯 (Demetrius Vikelas) 擔任首任國際奧委會主席,顧拜旦當選為秘書長。

      1896年4月5日,第一屆奧運會在希臘雅典舉行。開幕式上,希臘國王喬治高度贊揚了顧拜旦的貢獻。雅典奧運會后,維凱拉斯辭去奧委會主席職務,顧拜旦當選第二任國際奧委會主席。1912年在瑞典斯德哥爾摩舉行的第5屆奧運會上。顧拜旦針對當時體育競賽中的一些弊端,發表了著名的詩作《體育頌》,獲得了本屆奧運會文藝比賽的金質獎章。

      1913年,顧拜旦為國際奧委會設計了會徽、會旗。會旗圖案白底、無邊、上面有藍、黃、黑、綠、紅5個環環相扣的彩色圓環,她象征著大洲的團結以及全世界運動員以公正、坦率的比賽和友好的精神在奧林匹克運動會上相聚。此外,他還倡議燃放奧林匹克火焰、設立奧林匹克杯等。在確定奧林匹克運動會口號的問題上,顧拜旦最初覺得應以“團結、友好、和平”的口號來指導比賽。后來,他的一個朋友狄東神甫提出了“更快、更高、更強”的口號,得到顧拜旦的贊賞,認為它體現了人類永遠向上、不斷進取的偉大精神,以后便倡議它作為國際奧林匹克運動會的口號。1925年顧拜旦辭去了國際奧委會主席的職務。在他任職期間(1896年至1925年),國際奧委會成員由14個增加到40個,并先后成立了20多個國際專項運動聯合會。在他卸任后,被終生聘為國際奧委會名譽主席。1937年9月2日,他在瑞士日內瓦與世長辭。按其遺囑,他的心臟被安葬在了奧林匹克運動的發源地——希臘奧林匹亞的科羅努斯山下。

      顧拜旦不但是一個杰出的國際體育活動家,而且還是一個卓有成就的教育學家、歷史學家。他一生著有《1870年后的法國史》、《教育制度的改革》、《英國教育學》、《運動的指導原理》、《運動心理之理想》、《體育頌》等著作。其中,最有名的是他在1912年斯德哥爾摩奧運會期間發表的《體育頌》,并因此而獲得該屆奧運會金質獎章。

      在法國,有以顧拜旦命名的街道、體育場館。在法國國家奧委會的大廳里,矗立著顧拜旦的銅像。1999年12月17日,他獲得由《奧林匹克雜志》評選的“世紀體育領導人”稱號。 


    體育頌

     [本文于1912年斯德哥爾摩第五屆奧林匹克運動會文藝競賽中獲金質獎,當時作者顧拜旦用德文筆名發表。陳國平譯]

      啊,體育,天神的歡娛、生命的動力!你猝然降臨在灰蒙蒙的林間空地,受難者,激動不已,你像是容光煥發的使者,向暮年人微笑致意。你像高山之巔出現的晨曦,照亮了昏暗的大地。

      啊,體育,你就是美麗!你塑造的人體變得高尚還是卑鄙,要看它是被可恥的欲望引向墮落,還是由健康的力量悉心培育。沒有勻稱協調,便談不上什么美麗。你的作用無與倫比,可使二者和諧統一;使人體運動富有節律:使動作變得優美,柔中含有剛毅。

      啊,體育,你就是正義!你體現了社會生活中追求不到的公平合理。任何人不可超過速度一分一秒,逾越高度一分一厘,取得成功的關鍵,只能是體力與精神融為一體。

      啊,體育,你就是勇氣!肌肉用力的全部含義是敢于搏擊。若不為此,敏捷、強健有何用?肌肉發達有何益?我們所說的勇氣,不是冒險家押上全部賭注似的蠻干,而是經過慎重的深思熟慮。

      啊,體育,你就是榮譽!榮譽的贏得要公正無私,反之便毫無意義。有人耍弄見不得人的詭計,以此達到欺騙同伴的目的。他的內心深處卻受著恥辱的絞縊,有朝一日被人識破,就會落得名聲掃地。

      啊,體育,你就是樂趣!想起你,內心充滿歡喜,血液循環加劇,思路更加開闊,條理愈加清晰。你可使憂傷的人散心解悶,你可使快樂的人生活更加甜蜜。

      啊,體育,你就是培育人類的沃地!你通過最直接的途徑,增強人民體質,矯正畸形軀體;防病于未然,使運動員得到啟迪;希望后代茁壯有力,繼往開來,奪取桂冠的勝利。

      啊,體育,你就是進步!為人類的日新月異,身體和精神的改善要同時抓起。你規定良好的生活習慣,要求人們對過度行為引起警惕。你告誡人們遵守規則,發揮人類最大能力而無損健康的肌體。

      啊,體育,你就是和平!你在各民族間建立愉快的聯系。你在有節制、有組織、有技藝的體力較量中產生,使全世界的青年



      回復  更多評論
      

    主站蜘蛛池模板: 免费一区二区视频| 亚欧乱色国产精品免费视频| 国产免费一级高清淫曰本片| 日韩视频免费一区二区三区| 亚洲av成人综合网 | 久久精品国产免费一区| 亚洲一区日韩高清中文字幕亚洲 | 亚洲aⅴ无码专区在线观看春色| 综合在线免费视频| 亚洲H在线播放在线观看H| 国产精品无码一二区免费| 在线观看亚洲免费| 免费人成视频在线观看不卡| 国产一区二区免费| 亚洲午夜成人精品无码色欲| 午夜免费福利在线| 另类专区另类专区亚洲| 亚洲一二成人精品区| 一级毛片一级毛片免费毛片| 久久精品国产亚洲Aⅴ蜜臀色欲| 一边摸一边桶一边脱免费视频| 久久久久亚洲精品成人网小说| 91香蕉国产线在线观看免费| 久久亚洲国产伦理| 免费少妇a级毛片人成网| 久久精品一本到99热免费| 无码日韩人妻AV一区免费l| 九月丁香婷婷亚洲综合色| 亚洲高清视频免费| 亚洲a∨国产av综合av下载 | 国产精品免费看久久久香蕉 | 日韩免费码中文在线观看| 亚洲国产精品综合久久网络 | caoporn成人免费公开| 久久精品国产亚洲αv忘忧草| 亚洲AV日韩精品久久久久久| 亚洲国产日韩在线观频| 在免费jizzjizz在线播| 一区二区三区无码视频免费福利| 亚洲依依成人亚洲社区| 亚洲精品第一国产综合精品|