编程修养(四)
11、出錯(cuò)信息的處理
—————————
你會(huì)處理出錯(cuò)信息嗎?哦,它并不是簡(jiǎn)單的輸出。看下面的示例: if ( p == NULL ){
??????? printf ( "ERR: The pointer is NULL\n" );
??? }
????
告別學(xué)生時(shí)代的編程吧。這種編程很不利于維護(hù)和管理,出錯(cuò)信息或是提示信息,應(yīng)該統(tǒng)一處理,而不是像上面這樣,寫(xiě)成一個(gè)“硬編碼”。第10條對(duì)這方面的處理做了一部分說(shuō)明。如果要管理錯(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",?
??? };
??????????????????????????????
??? /* 聲明錯(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)槲募蜷_(kāi)失敗,A程序員出一個(gè)信息,B程序員又出一個(gè)信息。而且這樣做,非常容易維護(hù)。代碼也易讀。 當(dāng)然,物極必反,也沒(méi)有必要把所有的輸出都放到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次啊。這是多么的沒(méi)有效率的事啊。應(yīng)該把這個(gè)函數(shù)拿到循環(huán)體外,這樣只調(diào)用一次,效率得到了很大的提高。雖然,我們的編譯器會(huì)進(jìn)行優(yōu)化,會(huì)把循環(huán)體內(nèi)的不變的東西拿到循環(huán)外面,但是,你相信所有編譯器會(huì)知道哪些是不變的嗎?我覺(jué)得編譯器不可靠。最好還是自己動(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è)開(kāi)銷很大啊。把這個(gè)變量聲明成static吧,當(dāng)函數(shù)再次被調(diào)用時(shí),就會(huì)省去了分配內(nèi)存的開(kāi)銷,執(zhí)行效率也很好。
??? 13、函數(shù)名和變量名的命名
————————————
我看到許多程序?qū)ψ兞棵秃瘮?shù)名的取名很草率,特別是變量名,什么a,b,c,aa,bb,cc,還有什么flag1,flag2, cnt1, cnt2,這同樣是一種沒(méi)有“修養(yǎng)”的行為。即便加上好的注釋。好的變量名或是函數(shù)名,我認(rèn)為應(yīng)該有以下的規(guī)則:
????
??? 1) 直觀并且可以拼讀,可望文知意,不必“解碼”。?
??? 2) 名字的長(zhǎng)度應(yīng)該即要最短的長(zhǎng)度,也要能最大限度的表達(dá)其含義。
??? 3) 不要全部大寫(xiě),也不要全部小寫(xiě),應(yīng)該大小寫(xiě)都有,如:GetLocalHostName 或是 UserAccount。
??? 4) 可以簡(jiǎn)寫(xiě),但簡(jiǎn)寫(xiě)得要讓人明白,如: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)或開(kāi)發(fā)庫(kù)(如:MFC)的命名風(fēng)格保持一致。
???
14、函數(shù)的傳值和傳指針
————————————
向函數(shù)傳參數(shù)時(shí),一般而言,傳入非const的指針時(shí),就表示,在函數(shù)中要修改這個(gè)指針把指內(nèi)存中的數(shù)據(jù)。如果是傳值,那么無(wú)論在函數(shù)內(nèi)部怎么修改這個(gè)值,也影響不到傳過(guò)來(lái)的值,因?yàn)閭髦凳侵粌?nèi)存拷貝。 什么?你說(shuō)這個(gè)特性你明白了,好吧,讓我們看看下面的這個(gè)例程: void
GetVersion(char* pStr)
{
??? pStr = malloc(10);
??? strcpy ( pStr, "2.0" );
} main()
{
??? char* ver = NULL;
??? GetVersion ( ver );
??? ...
??? ...
??? free ( ver );
} 我保證,類似這樣的問(wèn)題是一個(gè)新手最容易犯的錯(cuò)誤。程序中妄圖通過(guò)函數(shù)GetVersion給指針ver分配空間,但這種方法根本沒(méi)有什么作用,原因就是——這是傳值,不是傳指針。你或許會(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除別人的程序,如果你覺(jué)得別人的程序有所不妥,請(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 );
??? /* ---------------------------------------- */
??? ... 當(dāng)然,這種方法是在軟件維護(hù)時(shí)使用的,這樣的方法,可以讓再維護(hù)的人很容易知道以前的代碼更改的動(dòng)作和意圖,而且這也是對(duì)原作者的一種尊敬。 以“注釋 — 添加”方式修改別人的程序,要好于直接刪除別人的程序。
本文轉(zhuǎn)自 haoel 51CTO博客,原文鏈接:http://blog.51cto.com/haoel/124710,如需轉(zhuǎn)載請(qǐng)自行聯(lián)系原作者
—————————
你會(huì)處理出錯(cuò)信息嗎?哦,它并不是簡(jiǎn)單的輸出。看下面的示例: if ( p == NULL ){
??????? printf ( "ERR: The pointer is NULL\n" );
??? }
????
告別學(xué)生時(shí)代的編程吧。這種編程很不利于維護(hù)和管理,出錯(cuò)信息或是提示信息,應(yīng)該統(tǒng)一處理,而不是像上面這樣,寫(xiě)成一個(gè)“硬編碼”。第10條對(duì)這方面的處理做了一部分說(shuō)明。如果要管理錯(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",?
??? };
??????????????????????????????
??? /* 聲明錯(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)槲募蜷_(kāi)失敗,A程序員出一個(gè)信息,B程序員又出一個(gè)信息。而且這樣做,非常容易維護(hù)。代碼也易讀。 當(dāng)然,物極必反,也沒(méi)有必要把所有的輸出都放到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次啊。這是多么的沒(méi)有效率的事啊。應(yīng)該把這個(gè)函數(shù)拿到循環(huán)體外,這樣只調(diào)用一次,效率得到了很大的提高。雖然,我們的編譯器會(huì)進(jìn)行優(yōu)化,會(huì)把循環(huán)體內(nèi)的不變的東西拿到循環(huán)外面,但是,你相信所有編譯器會(huì)知道哪些是不變的嗎?我覺(jué)得編譯器不可靠。最好還是自己動(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è)開(kāi)銷很大啊。把這個(gè)變量聲明成static吧,當(dāng)函數(shù)再次被調(diào)用時(shí),就會(huì)省去了分配內(nèi)存的開(kāi)銷,執(zhí)行效率也很好。
??? 13、函數(shù)名和變量名的命名
————————————
我看到許多程序?qū)ψ兞棵秃瘮?shù)名的取名很草率,特別是變量名,什么a,b,c,aa,bb,cc,還有什么flag1,flag2, cnt1, cnt2,這同樣是一種沒(méi)有“修養(yǎng)”的行為。即便加上好的注釋。好的變量名或是函數(shù)名,我認(rèn)為應(yīng)該有以下的規(guī)則:
????
??? 1) 直觀并且可以拼讀,可望文知意,不必“解碼”。?
??? 2) 名字的長(zhǎng)度應(yīng)該即要最短的長(zhǎng)度,也要能最大限度的表達(dá)其含義。
??? 3) 不要全部大寫(xiě),也不要全部小寫(xiě),應(yīng)該大小寫(xiě)都有,如:GetLocalHostName 或是 UserAccount。
??? 4) 可以簡(jiǎn)寫(xiě),但簡(jiǎn)寫(xiě)得要讓人明白,如: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)或開(kāi)發(fā)庫(kù)(如:MFC)的命名風(fēng)格保持一致。
???
14、函數(shù)的傳值和傳指針
————————————
向函數(shù)傳參數(shù)時(shí),一般而言,傳入非const的指針時(shí),就表示,在函數(shù)中要修改這個(gè)指針把指內(nèi)存中的數(shù)據(jù)。如果是傳值,那么無(wú)論在函數(shù)內(nèi)部怎么修改這個(gè)值,也影響不到傳過(guò)來(lái)的值,因?yàn)閭髦凳侵粌?nèi)存拷貝。 什么?你說(shuō)這個(gè)特性你明白了,好吧,讓我們看看下面的這個(gè)例程: void
GetVersion(char* pStr)
{
??? pStr = malloc(10);
??? strcpy ( pStr, "2.0" );
} main()
{
??? char* ver = NULL;
??? GetVersion ( ver );
??? ...
??? ...
??? free ( ver );
} 我保證,類似這樣的問(wèn)題是一個(gè)新手最容易犯的錯(cuò)誤。程序中妄圖通過(guò)函數(shù)GetVersion給指針ver分配空間,但這種方法根本沒(méi)有什么作用,原因就是——這是傳值,不是傳指針。你或許會(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除別人的程序,如果你覺(jué)得別人的程序有所不妥,請(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 );
??? /* ---------------------------------------- */
??? ... 當(dāng)然,這種方法是在軟件維護(hù)時(shí)使用的,這樣的方法,可以讓再維護(hù)的人很容易知道以前的代碼更改的動(dòng)作和意圖,而且這也是對(duì)原作者的一種尊敬。 以“注釋 — 添加”方式修改別人的程序,要好于直接刪除別人的程序。
本文轉(zhuǎn)自 haoel 51CTO博客,原文鏈接:http://blog.51cto.com/haoel/124710,如需轉(zhuǎn)載請(qǐng)自行聯(lián)系原作者
總結(jié)
- 上一篇: 使用注解实现ssh整合
- 下一篇: ubuntu下安装mongodb