- 論壇徽章:
- 0
|
關(guān)于編程風格的討論 PioneerMan(原作)
關(guān)鍵字 風格
**軟件公司軟件開發(fā)規(guī)范
(試行版)
在公司團隊協(xié)作開發(fā)的情況下,編程時應該強調(diào)的一個重要方面是程序的易讀性,在保證軟件的速度等性能指標能滿足用戶需求的情況下,能讓其他程序員容易讀懂你的程序。一套鮮明的編程風格,可以讓協(xié)作者、后繼者和自己一目了然,在很短的時間內(nèi)看清程序的結(jié)構(gòu),理解設(shè)計的思路。大大的提高代碼的可讀性、可重用性、程序健壯性、可移植性和可維護性。
制定本編程規(guī)范的目的是為了提高公司的軟件開發(fā)效率及所開發(fā)的軟件的可維護性,提高軟件的質(zhì)量。本規(guī)范由程序風格、命名規(guī)則、注釋規(guī)范、程序健壯性、可移植性、錯誤處理以及軟件的模塊化規(guī)范等部分組成。
一、程序風格:
1、嚴格采用階梯層次組織程序代碼:
各層次縮進的分格采用VC的缺省風格,即每層次縮進為4格,括號位于下一行。要求相匹配的大括號在同一列,對繼行則要求再縮進4格。例如:
void main()
{
......
long lI; //循環(huán)變量
long lSum;//用來記錄和
float fAvg;//用來求平均值
......
//對數(shù)進行累加。
for( lI=0;lI<10;lI++)
{
lSum=lSum+lI;
...... }
//求平均值。
fAvg=lSum/10.0;
......
}
2、提示信息字符串的位置
在程序中需要給出的提示字符串,為了支持多種語言的開發(fā),除了一些給調(diào)試用的臨時信息外,其他所有的提示信息必須定義在資源中。
3、對變量的定義,盡量位于函數(shù)的開始位置。
二、命名規(guī)則:
1、變量名的命名規(guī)則
①、變量的命名規(guī)則要求用“匈牙利法則”。即開頭字母用變量的類型,其余部分用變量的英文意思或其英文意思的縮寫,盡量避免用中文的拼音,要求單詞的第一個字母應大寫。
即: 變量名=變量類型+變量的英文意思(或縮寫)
對非通用的變量,在定義時加入注釋說明,變量定義盡量可能放在函數(shù)的開始處。
見下表:
bool(BOOL) 用b開頭 bIsParent
byte(BYTE) 用by開頭 byFlag
short(int) 用n開頭 nStepCount
long(LONG) 用l開頭 lSum
char(CHAR) 用c開頭 cCount
float(FLOAT) 用f開頭 fAvg
double(DOUBLE) 用d開頭 dDeta
void(VOID) 用v開頭 vVariant
unsigned short(WORD) 用w開頭 wCount
unsigned long(DWORD) 用dw開頭 dwBroad
HANDLE(HINSTANCE) 用h開頭 hHandle
DWORD 用dw開頭 dwWord
LPCSTR(LPCTSTR) 用str開頭 strString
用0結(jié)尾的字符串 用sz開頭 szFileName
對未給出的變量類型要求提出并給出命名建議給技術(shù)委員會。
②、指針變量命名的基本原則為:
對一重指針變量的基本原則為:
“p”+變量類型前綴+命名
如一個float*型應該表示為pfStat
對多重指針變量的基本規(guī)則為:
二重指針: “pp”+變量類型前綴+命名
三重指針: “ppp”+變量類型前綴+命名
......
③、全局變量用g_開頭,如一個全局的長型變量定義為g_lFailCount,即:變量名=g_+變量類型+變量的英文意思(或縮寫)
④、靜態(tài)變量用s_開頭,如一個靜態(tài)的指針變量定義為s_plPerv_Inst,即: 變量名=s_+變量類型+變量的英文意思(或縮寫)
⑤、成員變量用m_開頭,如一個長型成員變量定義為m_lCount;即:變量名=m_+變量類型+變量的英文意思(或縮寫)
⑥、對枚舉類型(enum)中的變量,要求用枚舉變量或其縮寫做前綴。并且要求用大寫。
如:enum cmEMDAYS
{
EMDAYS_MONDAY;
EMDAYS_TUESDAY;
……
};
⑦、對struct、union、class變量的命名要求定義的類型用大寫。并要加上前綴,其內(nèi)部變量的命名規(guī)則與變量命名規(guī)則一致。
結(jié)構(gòu)一般用S開頭
如:struct ScmNPoint
{
int nX;//點的X位置
int nY; //點的Y位置
};
聯(lián)合體一般用U開頭
如: union UcmLPoint
{
long lX;
long lY;
}
類一般用C開頭
如:
class CcmFPoint
{
public:
float fPoint;
};
對一般的結(jié)構(gòu)應該定義為類模板,為以后的擴展性考慮
如:
template <class TYPE>;
class CcmTVector3d
{
public:
TYPE x,y,z;
};
⑧、對常量(包括錯誤的編碼)命名,要求常量名用大寫,常量名用英文表達其意思。
如:#define CM_FILE_NOT_FOUND CMMAKEHR(0X20B) 其中CM表示類別。
⑨、對const 的變量要求在變量的命名規(guī)則前加入c_,即:c_+變量命名規(guī)則;例如:
const char* c_szFileName;
2、 函數(shù)的命名規(guī)范:
函數(shù)的命名應該盡量用英文表達出函數(shù)完成的功能。遵循動賓結(jié)構(gòu)的命名法則,函數(shù)名中動詞在前,并在命名前加入函數(shù)的前綴,函數(shù)名的長度不得少于8個字母。
例如:
long cmGetDeviceCount(……);
3、函數(shù)參數(shù)規(guī)范:
①、 參數(shù)名稱的命名參照變量命名規(guī)范。
②、 為了提高程序的運行效率,減少參數(shù)占用的堆棧,傳遞大結(jié)構(gòu)的參數(shù),一律采用指針或引用方式傳遞。
③、 為了便于其他程序員識別某個指針參數(shù)是入口參數(shù)還是出口參數(shù),同時便于編譯器檢查錯誤,應該在入口參數(shù)前加入const標志。如:
……cmCopyString(const char * c_szSource, char * szDest)
4、引出函數(shù)規(guī)范:
對于從動態(tài)庫引出作為二次開發(fā)函數(shù)公開的函數(shù),為了能與其他函數(shù)以及Windows的函數(shù)區(qū)分,采用類別前綴+基本命名規(guī)則的方法命名。例如:在對動態(tài)庫中引出的一個圖象編輯的函數(shù)定義為 imgFunctionname(其中img為image縮寫)。
現(xiàn)給出三種庫的命名前綴:
①、 對通用函數(shù)庫,采用cm為前綴。
②、 對三維函數(shù)庫,采用vr為前綴。
③、 對圖象函數(shù)庫,采用img為前綴。
對宏定義,結(jié)果代碼用同樣的前綴。
5、文件名(包括動態(tài)庫、組件、控件、工程文件等)的命名規(guī)范:
文件名的命名要求表達出文件的內(nèi)容,要求文件名的長度不得少于5個字母,嚴禁使用象file1,myfile之類的文件名。
三、注釋規(guī)范:
1、函數(shù)頭的注釋
對于函數(shù),應該從“功能”,“參數(shù)”,“返回值”、“主要思路”、“調(diào)用方法”、“日期”六個方面用如下格式注釋:
//程序說明開始
//================================================================//
// 功能: 從一個String 中刪除另一個String。
// 參數(shù): strByDelete,strToDelete
// (入口) strByDelete: 被刪除的字符串(原來的字符串)
// (出口) strToDelete: 要從上個字符串中刪除的字符串。
// 返回: 找到并刪除返回1,否則返回0。(對返回值有錯誤編碼的要// 求列出錯誤編碼)。
// 主要思路:本算法主要采用循環(huán)比較的方法來從strByDelete中找到
// 與strToDelete相匹配的字符串,對多匹配strByDelete
// 中有多個strToDelete子串)的情況沒有處理。請參閱:
// 書名......
// 調(diào)用方法:......
// 日期:起始日期,如:2000/8/21.9:40--2000/8/23.21:45
//================================================================//
函數(shù)名(……)
//程序說明結(jié)束
①、 對于某些函數(shù),其部分參數(shù)為傳入值,而部分參數(shù)為傳出值,所以對參數(shù)要詳細說明該參數(shù)是入口參數(shù),還是出口參數(shù),對于某些意義不明確的參數(shù)還要做詳細說明(例如:以角度作為參數(shù)時,要說明該角度參數(shù)是以弧度(PI),還是以度為單位),對既是入口又是出口的變量應該在入口和出口處同時標明。等等。
②、 函數(shù)的注釋應該放置在函數(shù)的頭文件中,在實現(xiàn)文件中的該函數(shù)的實現(xiàn)部分應該同時放置該注釋。
③、 在注釋中應該詳細說明函數(shù)的主要實現(xiàn)思路、特別要注明自己的一些想法,如果有必要則應該寫明對想法產(chǎn)生的來由。對一些模仿的函數(shù)應該注釋上函數(shù)的出處。
④、 在注釋中詳細注明函數(shù)的適當調(diào)用方法,對于返回值的處理方法等。在注釋中要強調(diào)調(diào)用時的危險方面,可能出錯的地方。
⑤、 對日期的注釋要求記錄從開始寫函數(shù)到結(jié)束函數(shù)的測試之間的日期。
⑥、 對函數(shù)注釋開始到函數(shù)命名之間應該有一組用來標識的特殊字符串。
如果算法比較復雜,或算法中的變量定義與位置有關(guān),則要求對變量的定義進行圖解。對難以理解的算法能圖解盡量圖解。
2、變量的注釋:
對于變量的注釋緊跟在變量的后面說明變量的作用。原則上對于每個變量應該注釋,但對于意義非常明顯的變量,如:i,j等循環(huán)變量可以不注釋。
例如: long lLineCount //線的根數(shù)。
3、文件的注釋:
文件應該在文件開頭加入以下注釋:
/////////////////////////////////////////////////////////////////////
// 工程: 文件所在的項目名。
// 作者:**,修改者:**
// 描述:說明文件的功能。
// 主要函數(shù):…………
// 版本: 說明文件的版本,完成日期。
// 修改: 說明對文件的修改內(nèi)容、修改原因以及修改日期。
// 參考文獻: ......
/////////////////////////////////////////////////////////////////////
為了頭文件被重復包含要求對頭文件進行定義如下:
#ifndef __FILENAME_H__
#define __FILENAME_H__
其中FILENAME為頭文件的名字。
4、其他注釋:
在函數(shù)內(nèi)我們不需要注釋每一行語句。但必須在各功能模塊的每一主要部分之前添加塊注釋,注釋每一組語句,在循環(huán)、流程的各分支等,盡可能多加以注釋。
其中的循環(huán)、條件、選擇等位置必須注釋。
對于前后順序不能顛倒的情況,建議在注釋中增加序號。
例如:
......
//1、......注釋
for (......)
{
}
if(......)
{//......注釋
}
else
{//......注釋
}
//......注釋
switch(......)
{
case: ......// ......注釋
......
case: ......// ......注釋
......
default: //......注釋
......
}
在其他順序執(zhí)行的程序中,每隔3-5行語句,必須加一個注釋,注明這一段語句所組成的小模塊的作用。對于自己的一些比較獨特的思想要求在注釋中標明。
四、程序健壯性:
1、函數(shù)的返回值規(guī)范:
對于函數(shù)的返回位置,盡量保持單一性,即一個函數(shù)盡量做到只有一個返回位置。(單入口單出口)。
要求大家統(tǒng)一函數(shù)的返回值,所有的函數(shù)的返回值都將以編碼的方式返回。
例如編碼定義如下:
#define CM_POINT_IS_NULL CMMAKEHR(0X200)
:
:
建議函數(shù)實現(xiàn)如下:
long 函數(shù)名(參數(shù),……)
{
long lResult; //保持錯誤號
lResult=CM_OK;
//如果參數(shù)有錯誤則返回錯誤號
if(參數(shù)==NULL)
{
lResult=CM_POINT_IS_NULL;
goto END;
}
……
END:
return lResult;
}
2、關(guān)于goto的應用:
對goto語句的應用,我們要求盡量少用goto語句。對一定要用的地方要求只能向后轉(zhuǎn)移。
3、資源變量的處理(資源變量是指消耗系統(tǒng)資源的變量):
對資源變量一定賦初值。分配的資源在用完后必須馬上釋放,并重新賦值。
例:
long * plAllocMem;//定義一個分配內(nèi)存的變量。
plAllocMem=(long*)calloc(40, sizeof( long ));//分配一段內(nèi)存。
//處理分配內(nèi)存錯誤
if(plAllocMem==NULL)
{
lResult=CM_MEM_ALLOC_FAILED;
goto END;
}
……
使用內(nèi)存
……
//釋放資源變量,并重新賦值。
if(pAllocMem!=NULL)
{
free(plAllocMem);
pAllocMem=NULL;
}
4、對復雜的條件判斷,為了程序的可讀性,應該盡量使用括號。
例:if(((szFileName!=NULL)&&(lCount>;=0)))||(bIsReaded==TRUE))
五、可移植性:
1、高質(zhì)量的代碼要求能夠跨平臺,所以我們的代碼應該考慮到對不同的平臺的支持,特別是對windows98和windowsnt的支持。
2、由于C語言的移植性比較好,所以對算法函數(shù)要求用C代碼,不能用C++代碼。
3、對不同的硬件與軟件的函數(shù)要做不同的處理。
五、錯誤處理:
1、錯誤報告處理。
編程中要求考慮函數(shù)的各種執(zhí)行情況,盡可能處理所有的流程情況。將函數(shù)分為兩類:
一類為與屏幕的顯示無關(guān),(不與用戶交換信息的函數(shù))
一類為與屏幕的顯示相關(guān)。(與用戶交換信息的函數(shù))
對于與屏幕顯示無關(guān)的函數(shù),函數(shù)通過返回值來報告錯誤。
對于與屏幕顯示有關(guān)的函數(shù),函數(shù)要負責向用戶發(fā)出警告,并進行錯誤處理。
錯誤處理代碼一般單獨建立通用處理函數(shù)。如下:
void cmDeal_With_Error(long ErrCode)
{
switch(ErrCode)
{
case 1://注釋
......
case 2://注釋
......
default://注釋
......
}
}
2、 盡早發(fā)現(xiàn)程序中的錯誤:
①、 重視編譯器中的警告信息。
對于編譯器產(chǎn)生的警告信息,我們應該引起足夠的重視,實際上許多警告信息指示了程序中潛在的錯誤危險。所以我們要認真檢查每一個警告信息,查看是否有某種隱患。盡量消除警告信息。
②、 利用斷言來檢查錯誤
對于程序中的某種假設(shè),或防止某些參數(shù)的非法值,利用斷言來幫助查錯是一種好的辦法。
例如下面的函數(shù):
long cmMemCpy(void * pvToMem, void* pvFromMem, size_t wSize)
{
……
if(pvToMem==NULL||pvFromMem==NULL)
{
lResult=CM _POINT_IS_NULL;
goto: END;
}
while(wSize-- >;0)
{
*pvToMem++=pvFromMem++;
}
END:
return lResult;
}
采用判斷可以檢查傳入的指針錯誤,但是這樣的判斷是程序最終的編譯代碼變大,同時降低了最終發(fā)布的程序的執(zhí)行效率。由于傳入空指針明顯是調(diào)用這函數(shù)的程序的錯誤,而不是這個函數(shù)的錯誤,我們可以考慮采用斷言來代替指針檢查,即用
ASSERT( pvToMem!=NULL&&pvFromMem!=NULL)
代替
if(pvToMem==NULL||pvFromMem==NULL)
{
lResult=CM_POINT_IS_NULL;
goto: END;
}
這樣只會在debug版中才會產(chǎn)生檢查代碼,而在正式發(fā)布版中不會帶有這些代碼。并且可以方便我們在程序調(diào)試中和測試時發(fā)現(xiàn)錯誤,同時又不影響程序的效率。
在下面的一些情況中必須加斷言:
a、 數(shù)的參數(shù),特別是指針參數(shù)必須利用斷言來進行確認。
b、 利用斷言檢查程序中的各種假設(shè)的正確性。
c、 在程序設(shè)計中不要輕易認為某種情況不可能發(fā)生,對你認為不可能發(fā)生的情況必須用斷言來證實。
為了使程序中的斷言發(fā)揮作用,所有用于在開發(fā)內(nèi)部進行測試或調(diào)試的動態(tài)庫、執(zhí)行程序、組件必須采用debug版。
說明:
在程序效率要求較高、或者調(diào)用比較頻繁的函數(shù),對入口參數(shù)的錯誤檢查,使用斷言方式,其優(yōu)點如上所敘,但其健壯性不強,所以在其他情況下,仍要求使用傳統(tǒng)的檢查方式,以增強程序的健壯性,當然,為了調(diào)試方便,可同時使用斷言方式。
③、 嚴格的測試:
對每一段代碼都要求進行嚴格的測試,特別對一些功能函數(shù)要對其各種臨界點(比如零值、無窮大的值等)進行測試。盡量做到每一段代碼零錯誤。
六、模塊化規(guī)范:
為了提高軟件的重用性,減少重復開發(fā)的工作量。同時也為了提高程序的可讀性,方便程序的維護,必須加強軟件的模塊化工作。模塊化應該遵循以下幾個基本規(guī)范:
1、 個函數(shù)應該作到精而小,函數(shù)的代碼應該控制在一個適度的規(guī)模,每個函數(shù)的代碼一般不能超過150行,如果超過這個規(guī)模,應該進行模塊化的工作。對于一些特殊的函數(shù)確實要超過150行,應該提交出來討論,通過后,要求編寫者更加詳細的對函數(shù)注釋,并寫明函數(shù)超行的原因,以及設(shè)計思想等。
2、 某一功能,如果重復實現(xiàn)三遍以上,既應該考慮模塊化,將其寫成通用函數(shù)。并向開發(fā)人員發(fā)布。并要求將接口文檔和實現(xiàn)的功能備案。
3、 每一個開發(fā)人員要盡可能的利用其他人的現(xiàn)成的模塊,減少重復開發(fā)。
4、 對函數(shù)進行模塊化時,要考慮函數(shù)的層次關(guān)系,特別是在增加新的功能模塊時,對原來的函數(shù)代碼要進行認真的調(diào)整,做到相同功能的不同函數(shù)沒有重復代碼,此要求的目的在于便于代碼維護。舉例如下:
現(xiàn)有如下函數(shù):
//從szFileName文件中取 ......
long ...... cmGetSomething(const char * c_szFileName,......)
{
CFile * pFile;//用來保存打開文件的地址
pFile=new CFile(c_szFileName,CFile::modeRead);//用創(chuàng)建一個只讀文件
if(pFile==NULL)
{
lResult=CM_POINT_IS_NULL;
goto END;
}
//從文件中讀取......
......
//關(guān)閉文件
delete pFile;
END:
return lResult;
}
若現(xiàn)在需要增加如下接口的新函數(shù):
long ...... cmReadSomething(CFile * pFile)
{
if(pFile==NULL)
{
lResult=CM_POINT_IS_NULL;
goto END;
}
//從文件中讀取......
......
END:
return lResult;
}
則要求如下:
將 long ......cmGetSomething(const char * c_szFileName,......)改為
long ...... cmGetSomething (const char * c_szFileName,......)
{
CFile * pFile; //用來保存打開文件的地址
long lResult=CM_OK;//錯誤返回碼
//打開文件
pFile=new CFile(c_szFileName,CFile::modeRead);
if(pFile==NULL)
{
lResult=CM_POINT_IS_NULL;
goto END;
}
//從文件中讀取......
lResult=cmReadSomething(pFile,......);
IF_ERROR_GOTO_END
//關(guān)閉文件
delete pFile;
END:
return lResult;
}
模塊化的一些注意事項:
① 、設(shè)計好模塊接口,用面向?qū)ο蟮挠^點看,包括:函數(shù)接口和變量接口。
② 、定義好接口以后不要輕易改動,并在模塊開頭(文件的開頭或函數(shù)的開頭)加以說明,所以在定義接口時,一定要反復琢磨,保持風格一致。
③ 、注意全局變量也是一種接口,如果不是確實必要,應該盡量少用全局變量。
④ 、在函數(shù)接口中,盡量使函數(shù)的接口容易理解和使用,其中每個輸入輸出參數(shù)都只代表一種類型數(shù)據(jù),不要把錯誤值和其他專用值混在函數(shù)的其他輸入輸出參數(shù)中。
⑤ 、爭取編寫出永遠成功的函數(shù),使調(diào)用者不必進行相應的錯誤處理。
此規(guī)范為試行版,解釋權(quán)屬于**軟件公司技術(shù)委員會!員工在編寫程序時請參閱FuncTemplate.h和FuncTemplate.cpp
FuncTemplate.h
//////////////////////////////////////////////////////////////////////
//工程: FuncTemplate.h //
//描述: 用來處理對二叉樹的一些算法,以及矩陣內(nèi)存分配。 //
//版本: FuncTemplate 1.0版。 //
//////////////////////////////////////////////////////////////////////
typedef struct _NODE //定義一個節(jié)點
{
struct _NODE * pLeftChild; //節(jié)點的左孩子
struct _NODE * pRightChild; //節(jié)點的右孩子
} NODE;
//==================================================================//
// 功能: 用循環(huán)來實現(xiàn)二叉樹的左序遍歷 //
// 參數(shù): pNode //
//(入口) pNode: 二叉樹的入口地址,即根節(jié)點的地址。 //
//(出口) 無。 //
// 返回: long 的函數(shù)返回碼,如果返回值為CM_OK,表成功遍歷,返回 //
// CM_POINT_IS_NULL表二叉樹指針為空。 //
//==================================================================//
long cmWalkTreeUseCycle(const NODE * pNode);
//==================================================================//
// 功能: 對矩陣進行分配內(nèi)存 //
// 參數(shù): wRowSize,wColSize,ppplMatrix //
//(入口) wRowSize:矩陣的行數(shù); //
//(入口) wColSize:矩陣的列數(shù); //
//(入口) ppplMatrix:要分配的矩陣的地址; //
//(出口) ppplMatrix:分配的矩陣的地址; //
// 返回: long 的錯誤號,如果返回值為CM_OK,表分配成功,返回 //
// CM_POINTER_IS_NOT_NULL表矩陣的入口地址值不為空。 //
// CM_MEM_ALLOC_FAILED 系統(tǒng)的內(nèi)存不足 //
//==================================================================//
long cmInitMatrix(const size_t wRowSize,const size_t wColSize,long *** ppplMatrix);
FuncTemplate.cpp
//////////////////////////////////////////////////////////////////////
//工程: FuncTemplate.cpp //
//作者: ** //
//修改者: **,*** //
//描述: 用來處理對二叉樹的一些算法,以及矩陣內(nèi)存分配。
//主要函數(shù):cmWalkTreeUseCycle,cmInitMatrix
//版本: FuncTemplate 1.0版。
//完成日期:2000-8-26 //
//修改日期: 2000-8-27,2001-12-21 //
//參考文獻: 圖形程序開發(fā)人員指南(機械工業(yè)出版社)
//////////////////////////////////////////////////////////////////////
#define STRICT
#include "stdio.h"
#include "stdlib.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include "makehresult.h"
#include "memory.h"
#include "functemplate.h"
#define CM_MEM_POINTER_IS_NULL CMEMAKEHR(0X100) //表示指針不為空的錯誤
#define CM_MEM_POINTER_IS_NOT_NULL CMEMAKEHR(0X101) //表示指針不為空的錯誤
#define MAX_PUSHED_NODES 100 //定義最大壓棧數(shù)量
////程序函數(shù)說明開始
//==================================================================
// 功能: 用循環(huán)來實現(xiàn)二叉樹的左序遍歷
// 參數(shù): cpNode //
//(入口) cpNode: 二叉樹的入口地址,即根節(jié)點的地址。
//(出口) 無。 //
// 返回: long 的函數(shù)返回碼,如果返回值為CM_OK,表成功遍歷,返回
// MS_POINT_IS_NULL表二叉樹指針為空。
// 調(diào)用方法:在調(diào)用此函數(shù)前必須先初始化二叉樹
// 思路: 如果正在訪問某節(jié)點,如果該節(jié)點有左分枝,就先訪問左分枝。
// 接著訪問該節(jié)點,如果該節(jié)點還有右分支。就再訪問右分支。
// 訪問左分枝時,在該處棧中做標記,當處理完左分枝就訪問此
// 處。訪問完每一個節(jié)點后,如果該節(jié)點沒有右孩子,并且棧已經(jīng)
// 為空,那么對該節(jié)點的訪問就完成,代碼對每一個節(jié)點都重復以
// 上操作。 //
// 參閱: 圖形程序開發(fā)人員指南(機械工業(yè)出版社)Page:927
// 日期: 2000/8/26.9:40--2000/8/26.21:45
//==================================================================
//圖解:
// 根節(jié)點
// @
// / \
// / \
// 左孩子@ @右孩子
// / \ / \
// / \ / \
// @ @ @ @
// 左孩子 右孩子左孩子 右孩子
HRESULT cmWalkTreeUseCycle(const NODE * cpNode)
////程序函數(shù)說明結(jié)束
{
HRESULT lResult;//用來保存返回的錯誤號。
NODE * pNodeStack[MAX_PUSHED_NODES];//用來作為節(jié)點的堆棧。
NODE ** ppNodeStack;//用來指示節(jié)點堆棧的指針。
lResult=CM_OK;
//判斷樹是否為空。
if(cpNode !=NULL)
{
pNodeStack[0]=NULL; //設(shè)置堆棧為空。
ppNodeStack=pNodeStack+1;
for(;;
{
//如果當前的節(jié)點有左孩子,對當前點壓棧。
//并把當前點移到左孩子,開始遍歷左子樹,
//如此,直到找到?jīng)]有左孩子的節(jié)點。
while (cpNode->;pLeftChild!=NULL)
{
*ppNodeStack++=(NODE*)cpNode;
cpNode=cpNode->;pLeftChild;
}
//我們現(xiàn)在處于沒有左孩子的節(jié)點,所以訪問
//節(jié)點,如果有右子樹,然后訪問右子樹。或
//后入的節(jié)點。重復節(jié)點的出棧直到我們找到
//有右子樹的節(jié)點,或所有節(jié)點出棧
for(;;
{
//訪問節(jié)點(調(diào)用別的函數(shù),沒有實現(xiàn))
cmVisitNode(cpNode);
//如果節(jié)點有右孩子,使該孩子成為當前節(jié)
//點并開始遍歷其子樹,否則回朔訪問節(jié)點
//直到我們發(fā)現(xiàn)一個有右子樹的節(jié)點,或所
//有的入棧點已經(jīng)被訪問。
if(cpNode->;pRightChild!=NULL)
{
cpNode=cpNode->;pRightChild;
break;
}
//出棧下一個節(jié)點,我們可以訪問它,并判
//斷是否有右子樹。
if((cpNode=*(--ppNodeStack))==NULL)
{
//棧為空并且當前節(jié)點沒有右子樹,完
//成遍歷。
lResult =CM_OK;
goto END;
}
}
}
}
//如果指針為空則設(shè)置返回值信息為MS_POINT_IS_NULL。
lResult=CM_POINTER_IS_NULL;
END:
return lResult;
}
////程序函數(shù)說明開始
//==================================================================
// 功能: 對矩陣進行分配內(nèi)存
// 參數(shù): cwRowSize,cwColSize,ppplMatrix
//(入口) cwRowSize:矩陣的行數(shù);
// cwColSize:矩陣的列數(shù);
// ppplMatrix:要分配的矩陣的地址;
//(出口) ppplMatrix:分配的矩陣的地址;
// 返回: long 的錯誤號,如果返回值為CM_OK,表分配成功,返回
// CM_POINTER_IS_NOT_NULL表矩陣的入口地址值不為空。
// CM_MEM_ALLOC_FAILED 系統(tǒng)的內(nèi)存不足
// 調(diào)用方法:在調(diào)用此函數(shù)前必須先對傳入值賦空。對傳入值作引用。
// 如:定義: long ** pplMatrix;對pplMatrix賦空,即:
// pplMatrix=NULL; 調(diào)用為:cmInitMatrix(10,10,&pplMatrix);
// 思路: 先對每一行分配內(nèi)存,再對每一行對應的列分配內(nèi)存。
// 參閱: 無
// 修改人:
// 日期: 2000/8/29.9:40--2000/8/29.16:45
//==================================================================
HRESULT cmInitMatrix(const size_t cwRowSize,const size_t cwColSize,long *** ppplMatrix)
////程序函數(shù)說明結(jié)束
{
HRESULT lResult; //存儲返回值。
lResult=CM_OK;
WORD wI; //循環(huán)變量
//要求對傳入值進行初始化NULL.
if(**ppplMatrix!=NULL)
{
lResult=CM_POINTER_IS_NOT_NULL;
goto END;
}
//對矩陣的行進行分配內(nèi)存
**ppplMatrix=(long*)malloc(cwRowSize*sizeof(long*));
//如果分配失敗則返回。
if((**ppplMatrix)==NULL)
{
lResult=CM_MEM_ALLOC_FAILED;
goto END;
}
//對每一行所擁有的列數(shù)進行分配內(nèi)存。
for(wI=0;wI<cwRowSize;wI++)
{
*ppplMatrix=(long**)malloc(cwColSize* sizeof(long));
//如果分配失敗則返回錯誤。
if(*ppplMatrix==NULL)
{
lResult=CM_MEM_ALLOC_FAILED;
goto END;
}
//對內(nèi)存置為零
memset(*ppplMatrix,0,cwColSize*sizeof(long));
}
END:
//對錯誤進行處理。
if(FAILED(lResult))
//如果分配不成功則釋放內(nèi)存。
if(**ppplMatrix!=NULL)
{
//對每一行內(nèi)存進行釋放。
for(wI=0;wI<cwRowSize;wI++)
{
if(*ppplMatrix!=NULL)
{
free(*ppplMatrix);
*ppplMatrix=NULL;
}
}
//對列的內(nèi)存進行釋放。
free(**ppplMatrix);
**ppplMatrix=NULL;
}
return lResult;
}
#ifdef __cplusplus
}
#endif /* __cplusplus */
錯誤處理擴展:
makehresult.h
//////////////////////////////////////////////////////////////////////////////
//作者: **
//描述: 用來對返回代碼的統(tǒng)一規(guī)定
//主要函數(shù):
//參考文獻:COM技術(shù)內(nèi)幕(微軟組件對象模型);[美]Dale Rogerson著
//////////////////////////////////////////////////////////////////////////////
#ifndef __ERRORDEFINE_H__
#define __ERRORDEFINE_H__
#include "winerror.h"
//圖解
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +---+-+-+-----------------------+-----------+-------------------+
// |Sev|C|R| 設(shè)備代碼 | 類編碼 | 返回代碼 |
// +---+-+-+-----------------------+-----------+-------------------+
//
//==================================================================================
////對設(shè)備代碼的定義
//對通用函數(shù)定義的設(shè)備代碼
#define FACILITY_CM 0x80
//對虛擬現(xiàn)實函數(shù)定義的設(shè)備代碼
#define FACILITY_VR 0x81
//對圖象函數(shù)定義的設(shè)備代碼
#define FACILITY_IMG 0x82
//===================================================================================
//定義類編碼(后16位中的前6位)
//從0X00~0X3F//
//其中0X38~0X3F為給程序員保留的臨時類編碼。
//標準類編碼從0X00~0X37
#define MEMORY_CLASS 0x01
#define FILE_CLASS 0x02
/*
#define TEMP_CLASS1 0X38
#define TEMP_CLASS2 0X39
#define TEMP_CLASS3 0X3A
#define TEMP_CLASS4 0X3B
#define TEMP_CLASS5 0X3C
#define TEMP_CLASS6 0X3D
#define TEMP_CLASS7 0X3E
#define TEMP_CLASS8 0X3F
*/
//===================================================================================
//制作臨時資源時位的前3位屏蔽(即為保存8個臨時類),臨時資源從0X0000~0X1FFF(程序員輸入值)
//實際為0XE000~0XFFFF(宏轉(zhuǎn)換后)
#define ADDTEMPCLASS(lResult) ((0X38<10)|lResult)
//定義返回通用函數(shù)的返回代碼類型
//對lResult的傳入值在0X0000~0X1FFF之間
#ifndef CMEMAKEHR
#define CMSMAKEHR(lResult) MAKE_HRESULT(SEVERITY_SUCCESS,FACILITY_CM,ADDTEMPCLASS(lResult))
#define CMEMAKEHR(lResult) MAKE_HRESULT(SEVERITY_ERROR,FACILITY_CM,ADDTEMPCLASS(lResult))
#endif //CMEMAKEHR
//定義返回虛擬現(xiàn)實函數(shù)的返回代碼類型
//對lResult的傳入值在0X0000~0X1FFF之間
#ifndef VREMAKEHR
#define VRSMAKEHR(lResult) MAKE_HRESULT(SEVERITY_SUCCESS,FACILITY_VR,ADDTEMPCLASS(lResult))
#define VREMAKEHR(lResult) MAKE_HRESULT(SEVERITY_ERROR,FACILITY_VR,ADDTEMPCLASS(lResult))
#endif //VREMAKEHR
//定義返回圖象函數(shù)的返回代碼類型
//對lResult的傳入值在0X0000~0X1FFF之間
#ifndef IMGEMAKEHR
#define IMGSMAKEHR(lResult) MAKE_HRESULT(SEVERITY_SUCCESS,FACILITY_IMG,ADDTEMPCLASS(lResult))
#define IMGEMAKEHR(lResult) MAKE_HRESULT(SEVERITY_ERROR,FACILITY_IMG,ADDTEMPCLASS(lResult))
#endif //IMGEMAKEHR
//當發(fā)生返回代碼時,轉(zhuǎn)到END處
//要求返回代碼號用lResult,標記號用END
#define IF_ERROR_GOTO_END if(FAILED(lResult)) goto END;
//顯示返回代碼的描述信息
#define DISPLAY_HRESULT_MESSAGE if(FAILED(lResult)) cmDispResultMessage(lResult);
typedef long HRESULT;
typedef long LRESULT;
//=======================================================================================================
//保持和windows系統(tǒng)(com)一致
#define CM_OK S_OK
#define CM_FALSE E_FAIL
#define VR_OK S_OK
#define VR_FALSE E_FAIL
#define IMG_OK S_OK
#define IMG_FALSE E_FAIL
#define CM_UNKNOW_ERROR CMEMAKEHR(0X000) //未知的錯誤
//內(nèi)存錯誤
#define CM_MEM_ALLOC_FAIL CMEMAKEHR(0X001) //內(nèi)存分配失敗
#define CM_MEM_FREE_FAIL CMEMAKEHR(0X002) //內(nèi)存釋放失敗
#define CM_INVALID_POINTER CMEMAKEHR(0X003) //無效的指針
//文件操作
#define CM_CREATE_FILE_FAIL CMEMAKEHR(0X010) //文件創(chuàng)建失敗
#define CM_OPEN_FILE_FAIL CMEMAKEHR(0X011) //文件打開失敗
#define CM_CLOSE_FILE_FAIL CMEMAKEHR(0X012) //文件關(guān)閉失敗
#define CM_DELETE_FILE_FAIL CMEMAKEHR(0X013) //文件刪除失敗
#define CM_FILE_HAS_EXISTED CMEMAKEHR(0X014) //文件已經(jīng)存在
#define CM_COPY_FILE_FAIL CMEMAKEHR(0X015) //文件拷貝失敗
#define CM_FILE_NOT_OPEN CMEMAKEHR(0X016) //文件沒有打開
#define CM_READ_FILE_FAIL CMEMAKEHR(0X017) //文件讀取失敗
#define CM_FIND_FILE_FAIL CMEMAKEHR(0X01 //文件查找失敗
#define CM_FILE_HAS_ERROR CMEMAKEHR(0X019) //文件本身有錯
#define CM_WRITE_FILE_FAIL CMEMAKEHR(0X020) //文件寫入失敗
//數(shù)組操作
#define CM_ARRAY_BEYOND CMEMAKEHR(0X060) //數(shù)組越界
//函數(shù)操作
#define CM_PARAM_BEYOND CMEMAKEHR(0X030) //參數(shù)越界
//數(shù)據(jù)庫操作
#define CM_OPEN_DATABASE_FAIL CMEMAKEHR(0X040) //數(shù)據(jù)庫打開失敗
#define CM_OPEN_TABLE_FAIL CMEMAKEHR(0X041) //數(shù)據(jù)表打開失敗
#define CM_TABLE_BEYOND CMEMAKEHR(0X042) //數(shù)據(jù)表訪問越界
#define CM_CREATE_DSN_FAIL CMEMAKEHR(0X043) //創(chuàng)建數(shù)據(jù)源失敗
#define CM_TABLE_EXIST CMEMAKEHR(0X044) //表已經(jīng)存在
#define CM_DATABASE_NOT_OPEN CMEMAKEHR(0X045) //數(shù)據(jù)庫沒有打開
//其他操作
#define CM_BEYOND_PARAM CMEMAKEHR(0x100) //參數(shù)越界
#define CM_POINT_IS_INVALIAD CMEMAKEHR(0x101) //點為非法的點
//圖象操作
#define CM_CREATE_BMP_FAIL CMEMAKEHR(0x200) //創(chuàng)建位圖失敗
//線程操作
#define CM_THREAD_IS_LOCK CMEMAKEHR(0x300) //線程背鎖住
//數(shù)學計算
#define CM_EDGENUM_NOTENOUGH CMEMAKEHR(0x500) //邊數(shù)不夠
//網(wǎng)絡(luò)操作
#define CM_CONNECT_FAIL CMEMAKEHR(0x700) //連接失敗
//三維設(shè)備操作
#define DD_CREATE_DEVICE_FAIL CMEMAKEHR(0x400) //創(chuàng)建設(shè)備失敗
#define DD_DELETE_DEVICE_FAIL CMEMAKEHR(0x401) //刪除設(shè)備失敗
#endif //__ERRORDEFINE_H__
#ifndef __ERRORMATCH_H__
#define __ERRORMATCH_H__
#include "errordefine.h"
#include "windows.h"
//errormatch.h
//用來對錯誤的匹配
#define lResultCount 128
#define IF_ERROR_DISPLAY_MESSAGE if(FAILED(lResult)) {DisplayChineseMessage(lResult); goto END;}
#ifndef SAFE_DELETE
#define SAFE_DELETE(p) {if(p) {delete (p);(p)=NULL;}}
#endif
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) {if(p) {delete[] (p);(p)=NULL;}}
#endif
//=======================================================================================================
typedef struct _RESULTINFOS //顯示返回代碼信息
{
long lResultNo; //返回代碼號
char szResultMessageChinese[40]; //返回代碼的中文描述
char szResultMessageEnglish[40]; //返回代碼的英文描述
}RESULTINFOS;
//=======================================================================================================
static RESULTINFOS sResultInfo[lResultCount]=
{
{CM_MEM_ALLOC_FAIL,"內(nèi)存分配失敗","Memeroy"},
{CM_MEM_FREE_FAIL,"內(nèi)存釋放失敗","Memeroy"},
{CM_INVALID_POINTER,"無效的指針","Memeroy"},
{CM_CREATE_FILE_FAIL,"內(nèi)存分配失敗","Memeroy"},
{CM_OPEN_FILE_FAIL,"文件打開失敗","Memeroy"},
{CM_CLOSE_FILE_FAIL,"文件關(guān)閉失敗","Memeroy"},
{CM_FILE_HAS_EXISTED,"文件已經(jīng)存在","Memeroy"},
{CM_COPY_FILE_FAIL,"文件拷貝失敗","Memeroy"},
{CM_FILE_NOT_OPEN,"文件沒有打開","Memeroy"},
{CM_READ_FILE_FAIL,"文件讀取失敗","Memeroy"},
{CM_FIND_FILE_FAIL,"文件查找失敗","Memeroy"},
{CM_FILE_HAS_ERROR,"文件本身有錯","Memeroy"},
{CM_WRITE_FILE_FAIL,"文件寫入失敗","Memeroy"},
{CM_ARRAY_BEYOND,"數(shù)組越界","Memeroy"},
{CM_PARAM_BEYOND,"參數(shù)越界","Memeroy"},
{CM_OPEN_DATABASE_FAIL,"數(shù)據(jù)庫打開失敗","Memeroy"},
{CM_OPEN_TABLE_FAIL,"數(shù)據(jù)表打開失敗","Memeroy"},
{CM_TABLE_BEYOND,"數(shù)據(jù)表訪問越界","Memeroy"},
{CM_CREATE_DSN_FAIL,"創(chuàng)建數(shù)據(jù)源失敗","Memeroy"},
{CM_TABLE_EXIST,"表已經(jīng)存在","Memeroy"},
{CM_DATABASE_NOT_OPEN,"數(shù)據(jù)庫沒有打開","Memeroy"},
{CM_CREATE_BMP_FAIL,"創(chuàng)建位圖失敗","Memeroy"},
{CM_THREAD_IS_LOCK,"線程背鎖住","Memeroy"},
{CM_POINT_IS_INVALIAD,"點為非法的點",""},
{DD_CREATE_DEVICE_FAIL,"創(chuàng)建設(shè)備失敗",""},
{DD_DELETE_DEVICE_FAIL,"刪除設(shè)備失敗",""}
};
static char * GetChineseMessage(HRESULT lResult)
{
for(long lI=0;lI<lResultCount;lI++)
{
if(sResultInfo[lI].lResultNo==lResult)
{
return sResultInfo[lI].szResultMessageChinese;
}
}
return NULL;
}
static char * GetEnglishMessage(HRESULT lResult)
{
for(long lI=0;lI<lResultCount;lI++)
{
if(sResultInfo[lI].lResultNo==lResult)
{
return sResultInfo[lI].szResultMessageEnglish;
}
}
return NULL;
}
static void DisplayChineseMessage(HRESULT lResult)
{
MessageBox(NULL,GetChineseMessage(lResult),"錯誤!",MB_OK);
}
static void DisplayEnglishMessage(HRESULT lResult)
{
MessageBox(NULL,GetEnglishMessage(lResult),"error!",MB_OK);
}
#endif//__ERRORMATCH_H__
|
|