關於程式設計風格的討論 (轉)

worldblog發表於2007-12-12
關於程式設計風格的討論 (轉)[@more@]

 
  **公司規範
 (試行版)
  在公司團隊協作開發的情況下,時應該強調的一個重要方面是的易讀性,在保證軟體的速度等指標能滿足需求的情況下,能讓其他程式設計師容易讀懂你的程式。一套鮮明的程式設計風格,可以讓協作者、後繼者和自己一目瞭然,在很短的時間內看清程式的結構,理解設計的思路。大大的提高程式碼的可讀性、可重用性、程式健壯性、可移植性和可維護性。
 制定本程式設計規範的目的是為了提高公司的軟體開發及所開發的軟體的可維護性,提高軟體的質量。本規範由程式風格、命名規則、註釋規範、程式健壯性、可移植性、錯誤處理以及軟體的模組化規範等部分組成。

一、程式風格:
  1、嚴格採用階梯層次組織程式程式碼:
各層次縮排的分格採用VC的預設風格,即每層次縮排為4格,括號位於下一行。要求相匹配的大括號在同一列,對繼行則要求再縮排4格。例如:
 void main()
 {
 ......
 long lI; //迴圈變數
 long lSum;//用來記錄和
 float fAvg;//用來求平均值
 ......
 //對數進行累加。
 for( lI=0;lI<10;lI++)
 {
 lSum=lSum+lI;
 ...... }
 //求平均值。
 fAvg=lSum/10.0;
 ......
 }

   2、提示資訊字串的位置
在程式中需要給出的提示字串,為了支援多種語言的開發,除了一些給用的臨時資訊外,其他所有的提示資訊必須定義在資源中。
3、對變數的定義,儘量位於的開始位置。
二、命名規則:
1、變數名的命名規則
①、變數的命名規則要求用“匈牙利法則”。即開頭字母用變數的型別,其餘部分用變數的英文意思或其英文意思的縮寫,儘量避免用中文的拼音,要求單詞的第一個字母應大寫。
 即: 變數名=變數型別+變數的英文意思(或縮寫)
 對非通用的變數,在定義時加入註釋說明,變數定義儘量可能放在函式的開始處。
 見下表:
 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() 用w開頭 wCount
unsigned long(DWORD) 用dw開頭 dwBroad
HANDLE(HINSTANCE) 用h開頭 hHandle
DWORD 用dw開頭 dwWord
LPCSTR(LPCTSTR) 用str開頭 strString
用0結尾的字串  用sz開頭  szFileName

對未給出的變數型別要求提出並給出命名建議給技術委員會。

②、指標變數命名的基本原則為:
對一重指標變數的基本原則為:
 “p”+變數型別字首+命名
如一個float*型應該表示為pfStat
對多重指標變數的基本規則為:
 二重指標:  “pp”+變數型別字首+命名
 三重指標:  “ppp”+變數型別字首+命名
 ......
③、全域性變數用g_開頭,如一個全域性的長型變數定義為g_lFailCount,即:變數名=g_+變數型別+變數的英文意思(或縮寫)
④、靜態變數用s_開頭,如一個靜態的指標變數定義為s_plPerv_Inst,即: 變數名=s_+變數型別+變數的英文意思(或縮寫)
⑤、成員變數用m_開頭,如一個長型成員變數定義為m_lCount;即:變數名=m_+變數型別+變數的英文意思(或縮寫)
⑥、對列舉型別(enum)中的變數,要求用列舉變數或其縮寫做字首。並且要求用大寫。
 如:enum cmEMDAYS
 {
 EMDAYS_MONDAY;
 EMDAYS_TUESDAY;
 ……
};
⑦、對struct、union、class變數的命名要求定義的型別用大寫。並要加上字首,其內部變數的命名規則與變數命名規則一致。
結構一般用S開頭
如:struct ScmNPoint
{
 int nX;//點的X位置
 int nY; //點的Y位置
};
 聯合體一般用U開頭
 如: union UcmLPoint
{
 long lX;
long lY;
}
類一般用C開頭
如:
class CcmFPoint
{
public:
float fPoint;
};
 對一般的結構應該定義為類别範本,為以後的擴充套件性考慮
 如:
 template
 class  CcmTVector3d
 {
 public:
  TYPE x,y,z;
 };
⑧、對常量(包括錯誤的編碼)命名,要求常量名用大寫,常量名用英文表達其意思。
 如:#define CM_FILE_NOT_FOUND  CMMAKEHR(0X20B)  其中CM表示類別。
⑨、對const 的變數要求在變數的命名規則前加入c_,即:c_+變數命名規則;例如:
  const char*  c_szFileName;
2、 函式的命名規範:
函式的命名應該儘量用英文表達出函式完成的功能。遵循動賓結構的命名法則,函式名中動詞在前,並在命名前加入函式的字首,函式名的長度不得少於8個字母。
例如:
 long cmGetDeviceCount(……);
3、函式引數規範:
①、 引數名稱的命名參照變數命名規範。
②、 為了提高程式的執行效率,減少引數佔用的堆疊,傳遞大結構的引數,一律採用指標或引用方式傳遞。
③、 為了便於其他程式設計師識別某個指標引數是入口引數還是出口引數,同時便於檢查錯誤,應該在入口引數前加入const標誌。如:
……cmCopyString(const char * c_sz,  char * szDest)
4、引出函式規範:
對於從動態庫引出作為二次開發函式公開的函式,為了能與其他函式以及的函式區分,採用類別字首+基本命名規則的方法命名。例如:在對動態庫中引出的一個圖象編輯的函式定義為 imgFunctionname(其中img為image縮寫)。
現給出三種庫的命名字首:
①、 對通用函式庫,採用cm為字首。
②、 對三維函式庫,採用vr為字首。
③、 對圖象函式庫,採用img為字首。
對宏定義,結果程式碼用同樣的字首。
 5、名(包括動態庫、、、工程檔案等)的命名規範:
檔名的命名要求表達出檔案的內容,要求檔名的長度不得少於5個字母,嚴禁使用象file1,myfile之類的檔名。
三、註釋規範:
1、函式頭的註釋
對於函式,應該從“功能”,“引數”,“返回值”、“主要思路”、“方法”、“日期”六個方面用如下格式註釋:
//程式說明開始
//================================================================//
//   功能:  從一個String 中刪除另一個String。
// 引數:  strByDelete,strToDelete
// (入口) strByDelete:  被刪除的字串(原來的字串)
// (出口) strToDelete:  要從上個字串中刪除的字串。
// 返回: 找到並刪除返回1,否則返回0。(對返回值有錯誤編碼的要// 求列出錯誤編碼)。
// 主要思路:本演算法主要採用迴圈比較的方法來從strByDelete中找到
//  與strToDelete相匹配的字串,對多匹配strByDelete
//  中有多個strToDelete子串)的情況沒有處理。請參閱:
//  書名......
// 呼叫方法:......
// 日期:起始日期,如:2000/8/21.9:40--2000/8/23.21:45
//================================================================//
函式名(……)
//程式說明結束
①、 對於某些函式,其部分引數為傳入值,而部分引數為傳出值,所以對引數要詳細說明該引數是入口引數,還是出口引數,對於某些意義不明確的引數還要做詳細說明(例如:以角度作為引數時,要說明該角度引數是以弧度(PI),還是以度為單位),對既是入口又是出口的變數應該在入口和出口處同時標明。等等。
②、 函式的註釋應該放置在函式的標頭檔案中,在實現檔案中的該函式的實現部分應該同時放置該註釋。
③、 在註釋中應該詳細說明函式的主要實現思路、特別要註明自己的一些想法,如果有必要則應該寫明對想法產生的來由。對一些模仿的函式應該註釋上函式的出處。
④、 在註釋中詳細註明函式的適當呼叫方法,對於返回值的處理方法等。在註釋中要強調呼叫時的危險方面,可能出錯的地方。
⑤、 對日期的註釋要求記錄從開始寫函式到結束函式的測試之間的日期。
⑥、 對函式註釋開始到函式命名之間應該有一組用來標識的特殊字串。
如果演算法比較複雜,或演算法中的變數定義與位置有關,則要求對變數的定義進行圖解。對難以理解的演算法能圖解儘量圖解。
2、變數的註釋:
對於變數的註釋緊跟在變數的後面說明變數的作用。原則上對於每個變數應該註釋,但對於意義非常明顯的變數,如:i,j等迴圈變數可以不註釋。
例如: long lLineCount  //線的根數。
  3、檔案的註釋:
檔案應該在檔案開頭加入以下注釋:
/////////////////////////////////////////////////////////////////////
//   工程:  檔案所在的專案名。
//   作者:**,修改者:**
//   描述:說明檔案的功能。
//   主要函式:…………
//   版本: 說明檔案的版本,完成日期。
//   修改: 說明對檔案的修改內容、修改原因以及修改日期。
//   參考文獻: ......
/////////////////////////////////////////////////////////////////////
為了標頭檔案被重複包含要求對標頭檔案進行定義如下:
 #ifndef __FILENAME_H__
#define __FILENAME_H__
其中FILENAME為標頭檔案的名字。
   4、其他註釋:
在函式內我們不需要註釋每一行語句。但必須在各功能模組的每一主要部分之前新增塊註釋,註釋每一組語句,在迴圈、流程的各分支等,儘可能多加以註釋。
 其中的迴圈、條件、選擇等位置必須註釋。
 對於前後順序不能顛倒的情況,建議在註釋中增加序號。
例如:
 ......
//1、......註釋
for (......)
{
}
if(......)
{//......註釋
}
else
{//......註釋
}
//......註釋
switch(......)
{
case: ......// ......註釋
......
case: ......// ......註釋
......
default:  //......註釋
......
}
在其他順序的程式中,每隔3-5行語句,必須加一個註釋,註明這一段語句所組成的小模組的作用。對於自己的一些比較獨特的思想要求在註釋中標明。
四、程式健壯性:
1、函式的返回值規範:
對於函式的返回位置,儘量保持單一性,即一個函式儘量做到只有一個返回位置。(單入口單出口)。
要求大家統一函式的返回值,所有的函式的返回值都將以編碼的方式返回。
例如編碼定義如下:
#define CM_POINT_IS_NULL CMMAKEHR(0X200)
 :
 :
建議函式實現如下:
 long 函式名(引數,……)
{
 long  lResult; //保持錯誤號
 lResult=CM_OK;
//如果引數有錯誤則返回錯誤號
 if(引數==NULL)
{
 lResult=CM_POINT_IS_NULL;
 goto END;
}
……
END:
return  lResult;
}
 2、關於goto的應用:
對goto語句的應用,我們要求儘量少用goto語句。對一定要用的地方要求只能向後轉移。
 3、資源變數的處理(資源變數是指消耗資源的變數):
 對資源變數一定賦初值。分配的資源在用完後必須馬上釋放,並重新賦值。
 例:
 long * plAllocMem;//定義一個分配的變數。
 plAllocMem=(long*)calloc(40, sizeof( long ));//分配一段記憶體。
 //處理分配記憶體錯誤
 if(plAllocMem==NULL)
 {
 lResult=CM_MEM_ALLOC_FAILED;
 goto END;
}
 ……
 使用記憶體
 ……
 //釋放資源變數,並重新賦值。
 if(pAllocMem!=NULL)
 {
 free(plAllocMem);
 pAllocMem=NULL;
}
 4、對複雜的條件判斷,為了程式的可讀性,應該儘量使用括號。
 例:if(((szFileName!=NULL)&&(lCount>=0)))||(bIsReaded==TRUE))
五、可移植性:
1、高質量的程式碼要求能夠跨平臺,所以我們的程式碼應該考慮到對不同的平臺的支援,特別是對windows98和windowsnt的支援。
 2、由於C語言的移植性比較好,所以對演算法函式要求用C程式碼,不能用C++程式碼。
 3、對不同的與軟體的函式要做不同的處理。
五、錯誤處理:
1、錯誤報告處理。
程式設計中要求考慮函式的各種執行情況,儘可能處理所有的流程情況。將函式分為兩類:
 一類為與螢幕的顯示無關,(不與使用者資訊的函式)
 一類為與螢幕的顯示相關。(與使用者交換資訊的函式)
對於與螢幕顯示無關的函式,函式透過返回值來報告錯誤。
對於與螢幕顯示有關的函式,函式要負責向使用者發出警告,並進行錯誤處理。
錯誤處理程式碼一般單獨建立通用處理函式。如下:
 void cmDeal_With_Error(long ErrCode)
{
 switch(ErrCode)
 {
 case 1://註釋
 ......
 case 2://註釋
 ......
 default://註釋
 ......
}
}
 
2、 儘早發現程式中的錯誤:
①、 重視編譯器中的警告資訊。
對於編譯器產生的警告資訊,我們應該引起足夠的重視,實際上許多警告資訊指示了程式中潛在的錯誤危險。所以我們要認真檢查每一個警告資訊,檢視是否有某種隱患。儘量消除警告資訊。
②、 利用斷言來檢查錯誤
對於程式中的某種假設,或防止某些引數的值,利用斷言來幫助查錯是一種好的辦法。
 例如下面的函式:
  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;
}
採用判斷可以檢查傳入的指標錯誤,但是這樣的判斷是程式最終的編譯程式碼變大,同時降低了最終釋出的程式的執行效率。由於傳入空指標明顯是呼叫這函式的程式的錯誤,而不是這個函式的錯誤,我們可以考慮採用斷言來代替指標檢查,即用
ASSERT( pvToMem!=NULL&&pvFromMem!=NULL)
代替
if(pvToMem==NULL||pvFromMem==NULL)
{
 lResult=CM_POINT_IS_NULL;
goto: END;
}
這樣只會在de版中才會產生檢查程式碼,而在正式釋出版中不會帶有這些程式碼。並且可以方便我們在程式除錯中和測試時發現錯誤,同時又不影響程式的效率。
 在下面的一些情況中必須加斷言:
a、 數的引數,特別是指標引數必須利用斷言來進行確認。
b、 利用斷言檢查程式中的各種假設的正確性。
c、 在中不要輕易認為某種情況不可能發生,對你認為不可能發生的情況必須用斷言來證實。
為了使程式中的斷言發揮作用,所有用於在開發內部進行測試或除錯的動態庫、執行程式、元件必須採用debug版。
 說明:
在程式效率要求較高、或者呼叫比較頻繁的函式,對入口引數的錯誤檢查,使用斷言方式,其優點如上所敘,但其健壯性不強,所以在其他情況下,仍要求使用傳統的檢查方式,以增強程式的健壯性,當然,為了除錯方便,可同時使用斷言方式。
③、 嚴格的測試:
對每一段程式碼都要求進行嚴格的測試,特別對一些功能函式要對其各種臨界點(比如零值、無窮大的值等)進行測試。儘量做到每一段程式碼零錯誤。
六、模組化規範:
為了提高軟體的重用性,減少重複開發的工作量。同時也為了提高程式的可讀性,方便程式的維護,必須加強軟體的模組化工作。模組化應該遵循以下幾個基本規範:
1、 個函式應該作到精而小,函式的程式碼應該控制在一個適度的規模,每個函式的程式碼一般不能超過150行,如果超過這個規模,應該進行模組化的工作。對於一些特殊的函式確實要超過150行,應該提交出來討論,透過後,要求編寫者更加詳細的對函式註釋,並寫明函式超行的原因,以及設計思想等。
2、 某一功能,如果重複實現三遍以上,既應該考慮模組化,將其寫成通用函式。並向開發人員釋出。並要求將介面文件和實現的功能備案。
3、 每一個開發人員要儘可能的利用其他人的現成的模組,減少重複開發。
4、 對函式進行模組化時,要考慮函式的層次關係,特別是在增加新的功能模組時,對原來的函式程式碼要進行認真的調整,做到相同功能的不同函式沒有重複程式碼,此要求的目的在於便於程式碼維護。舉例如下:
現有如下函式:
//從szFileName檔案中取 ......
long ...... cmGetSomething(const char * c_szFileName,......)
{
 CFile * pFile;//用來儲存開啟檔案的地址
 pFile=new CFile(c_szFileName,CFile::modeRead);//用建立一個只讀檔案
if(pFile==NULL)
{
 lResult=CM_POINT_IS_NULL;
 goto END;
}
//從檔案中讀取......
......
//關閉檔案
delete pFile;
 END:
return lResult;
}
 若現在需要增加如下介面的新函式:
 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
//關閉檔案
delete pFile;
END:
return lResult;
}

模組化的一些注意事項:
① 、設計好模組介面,用面向的觀點看,包括:函式介面和變數介面。
② 、定義好介面以後不要輕易改動,並在模組開頭(檔案的開頭或函式的開頭)加以說明,所以在定義介面時,一定要反覆琢磨,保持風格一致。
③ 、注意全域性變數也是一種介面,如果不是確實必要,應該儘量少用全域性變數。
④ 、在函式介面中,儘量使函式的介面容易理解和使用,其中每個輸入輸出引數都只代表一種型別資料,不要把錯誤值和其他專用值混在函式的其他輸入輸出引數中。
⑤ 、爭取編寫出永遠成功的函式,使呼叫者不必進行相應的錯誤處理。
此規範為試行版,解釋權屬於**軟體公司技術委員會!員工在編寫程式時請參閱FuncTemplate.h和FuncTemplate.cpp

FuncTemplate.h

//////////////////////////////////////////////////////////////////////
//工程: FuncTemplate.h   //
//描述: 用來處理對二叉樹的一些演算法,以及矩陣記憶體分配。 //
//版本: FuncTemplate 1.0版。 //
//////////////////////////////////////////////////////////////////////

 

 

typedef struct _NODE //定義一個節點
{
 struct _NODE * pLeftChild; //節點的左孩子
 struct _NODE * pRightChild; //節點的右孩子
} NODE;

//==================================================================//
// 功能: 用迴圈來實現二叉樹的左序遍歷 //
// 引數: pNode //
//(入口) pNode:  二叉樹的入口地址,即根節點的地址。 //
//(出口) 無。 //
// 返回: long 的函式返回碼,如果返回值為CM_OK,表成功遍歷,返回 //
// CM_POINT_IS_NULL表二叉樹指標為空。 //
//==================================================================//
long cmWalkTreeUseCycle(const NODE * pNode);

//==================================================================//
// 功能: 對矩陣進行分配記憶體 //
// 引數: wRowSize,wColSize,ppplMatrix //
//(入口) wRowSize:矩陣的行數;  //
//(入口) wColSize:矩陣的列數;  //
//(入口) ppplMatrix:要分配的矩陣的地址; //
//(出口) ppplMatrix:分配的矩陣的地址; //
// 返回: long 的錯誤號,如果返回值為CM_OK,表分配成功,返回 //
// CM_POINTER_IS_NOT_NULL表矩陣的入口地址值不為空。 //
// CM_MEM_ALLOC_FAILED 系統的記憶體不足 //
//==================================================================//
long cmInitMatrix(const size_t wRowSize,const size_t wColSize,long *** ppplMatrix);

 

FuncTemplate.cpp

//////////////////////////////////////////////////////////////////////
//工程: FuncTemplate.cpp //
//作者: ** //
//修改者: **,*** //
//描述: 用來處理對二叉樹的一些演算法,以及矩陣記憶體分配。 
//主要函式:cmWalkTreeUseCycle,cmInitMatrix 
//版本: FuncTemplate 1.0版。 
//完成日期:2000-8-26 //
//修改日期: 2000-8-27,2001-12-21 //
//參考文獻: 圖形程式開發人員指南(機械工業出版社) 
//////////////////////////////////////////////////////////////////////

#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  //定義最大壓棧數量


////程式函式說明開始
//==================================================================
// 功能: 用迴圈來實現二叉樹的左序遍歷 
// 引數: cpNode //
//(入口) cpNode:  二叉樹的入口地址,即根節點的地址。 
//(出口) 無。 //
// 返回: long 的函式返回碼,如果返回值為CM_OK,表成功遍歷,返回 
// MS_POINT_IS_NULL表二叉樹指標為空。 
// 呼叫方法:在呼叫此函式前必須先初始化二叉樹 
// 思路: 如果正在訪問某節點,如果該節點有左分枝,就先訪問左分枝。
// 接著訪問該節點,如果該節點還有右分支。就再訪問右分支。 
// 訪問左分枝時,在該處棧中做標記,當處理完左分枝就訪問此 
// 處。訪問完每一個節點後,如果該節點沒有右孩子,並且棧已經
// 為空,那麼對該節點的訪問就完成,程式碼對每一個節點都重複以
// 上操作。 //
// 參閱: 圖形程式開發人員指南(機械工業出版社)Page:927 
// 日期: 2000/8/26.9:40--2000/8/26.21:45 
//==================================================================

//圖解:
//  根節點
//  @
//  / 
//  / 
//  左孩子@  @右孩子
//  /   /
//  /    / 
//  @  @  @  @
//  左孩子 右孩子左孩子 右孩子

HRESULT cmWalkTreeUseCycle(const NODE * cpNode) 
////程式函式說明結束
{
 HRESULT lResult;//用來儲存返回的錯誤號。
 NODE * pNodeStack[MAX_PUSHED_NODES];//用來作為節點的堆疊。
 NODE ** ppNodeStack;//用來指示節點堆疊的指標。

 lResult=CM_OK;
 //判斷樹是否為空。
 if(cpNode !=NULL)
 {
 pNodeStack[0]=NULL; //設定堆疊為空。
 ppNodeStack=pNodeStack+1;
 for(;;)
 {
 //如果當前的節點有左孩子,對當前點壓棧。
 //並把當前點移到左孩子,開始遍歷左子樹,
 //如此,直到找到沒有左孩子的節點。
 while (cpNode->pLeftChild!=NULL)
 {
 *ppNodeStack++=(NODE*)cpNode;
 cpNode=cpNode->pLeftChild;
 }
 
 //我們現在處於沒有左孩子的節點,所以訪問
 //節點,如果有右子樹,然後訪問右子樹。或
 //後入的節點。重複節點的出棧直到我們找到
 //有右子樹的節點,或所有節點出棧
 for(;;)
 {
 //訪問節點(呼叫別的函式,沒有實現)
 cmVisitNode(cpNode);

 //如果節點有右孩子,使該孩子成為當前節
 //點並開始遍歷其子樹,否則回朔訪問節點
 //直到我們發現一個有右子樹的節點,或所
 //有的入棧點已經被訪問。
 if(cpNode->pRightChild!=NULL)
 {
 cpNode=cpNode->pRightChild;
 break;
 }

 //出棧下一個節點,我們可以訪問它,並判
 //斷是否有右子樹。
 if((cpNode=*(--ppNodeStack))==NULL)
 {
 //棧為空並且當前節點沒有右子樹,完
 //成遍歷。
 lResult =CM_OK;
 goto END;
 }
 }
 }
 }
 //如果指標為空則設定返回值資訊為MS_POINT_IS_NULL。
 lResult=CM_POINTER_IS_NULL;
END:
 return lResult;
}

 


////程式函式說明開始
//==================================================================
// 功能: 對矩陣進行分配記憶體 
// 引數: cwRowSize,cwColSize,ppplMatrix 
//(入口) cwRowSize:矩陣的行數;  
// cwColSize:矩陣的列數;  
// ppplMatrix:要分配的矩陣的地址; 
//(出口) ppplMatrix:分配的矩陣的地址; 
// 返回: long 的錯誤號,如果返回值為CM_OK,表分配成功,返回 
// CM_POINTER_IS_NOT_NULL表矩陣的入口地址值不為空。 
// CM_MEM_ALLOC_FAILED 系統的記憶體不足 
// 呼叫方法:在呼叫此函式前必須先對傳入值賦空。對傳入值作引用。 
// 如:定義: long ** pplMatrix;對pplMatrix賦空,即: 
// pplMatrix=NULL; 呼叫為:cmInitMatrix(10,10,&pplMatrix);
// 思路: 先對每一行分配記憶體,再對每一行對應的列分配記憶體。 
// 參閱: 無 
// 修改人:
// 日期: 2000/8/29.9:40--2000/8/29.16:45 
//==================================================================


HRESULT cmInitMatrix(const size_t cwRowSize,const size_t cwColSize,long *** ppplMatrix)
////程式函式說明結束
{
 HRESULT lResult; //返回值。
 lResult=CM_OK;

 WORD  wI; //迴圈變數
 
 //要求對傳入值進行初始化NULL.
 if(**ppplMatrix!=NULL)
 {
 lResult=CM_POINTER_IS_NOT_NULL;
 goto END;
 }
 //對矩陣的行進行分配記憶體
 **ppplMatrix=(long*)malloc(cwRowSize*sizeof(long*));
 //如果分配失敗則返回。
 if((**ppplMatrix)==NULL)
 {
 lResult=CM_MEM_ALLOC_FAILED;
 goto END;
 }
 
 //對每一行所擁有的列數進行分配記憶體。
 for(wI=0;wI {
 *ppplMatrix=(long**)malloc(cwColSize* sizeof(long));
 //如果分配失敗則返回錯誤。
 if(*ppplMatrix==NULL)
 {
 lResult=CM_MEM_ALLOC_FAILED;
 goto END;
 }
 //對記憶體置為零
 memset(*ppplMatrix,0,cwColSize*sizeof(long));
 }
 

END:
 //對錯誤進行處理。
 if(FAILED(lResult))
 //如果分配不成功則釋放記憶體。
 if(**ppplMatrix!=NULL)
 {
 //對每一行記憶體進行釋放。
 for(wI=0;wI {
 if(*ppplMatrix!=NULL)
 {
 free(*ppplMatrix);
 *ppplMatrix=NULL;
 }
 }
 //對列的記憶體進行釋放。
 free(**ppplMatrix);
 **ppplMatrix=NULL;
 }
 return lResult;
}

#ifdef __cplusplus
}
#endif  /* __cplusplus */

錯誤處理擴充套件:

makehresult.h

//////////////////////////////////////////////////////////////////////////////
//作者: **
//描述: 用來對返回程式碼的統一規定
//主要函式:
//參考文獻:COM技術內幕(元件物件模型);[美]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| 裝置程式碼  |  類編碼 |  返回程式碼  |
//  +---+-+-+-----------------------+-----------+-------------------+
//

//==================================================================================
////對裝置程式碼的定義
//對通用函式定義的裝置程式碼
#define FACILITY_CM 0x80
//對虛擬現實函式定義的裝置程式碼
#define FACILITY_VR 0x81
//對圖象函式定義的裝置程式碼
#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(宏轉換後)
#define ADDTEMPCLASS(lResult) ((0X38<10)|lResult)


//定義返回通用函式的返回程式碼型別
//對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


//定義返回虛擬現實函式的返回程式碼型別
//對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


//定義返回圖象函式的返回程式碼型別
//對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

//當發生返回程式碼時,轉到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系統(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) //未知的錯誤

//記憶體錯誤
#define CM_MEM_ALLOC_FAIL CMEMAKEHR(0X001) //記憶體分配失敗
#define CM_MEM_FREE_FAIL CMEMAKEHR(0X002) //記憶體釋放失敗
#define CM_INVALID_POINTER CMEMAKEHR(0X003) //無效的指標


//檔案操作
#define CM_CREATE_FILE_FAIL CMEMAKEHR(0X010) //檔案建立失敗
#define CM_OPEN_FILE_FAIL CMEMAKEHR(0X011) //檔案開啟失敗
#define CM_CLOSE_FILE_FAIL CMEMAKEHR(0X012) //檔案關閉失敗
#define CM_DELETE_FILE_FAIL CMEMAKEHR(0X013) //檔案刪除失敗
#define CM_FILE_HAS_EXISTED CMEMAKEHR(0X014) //檔案已經存在
#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(0X018) //檔案查詢失敗
#define CM_FILE_HAS_ERROR CMEMAKEHR(0X019) //檔案本身有錯
#define CM_WRITE_FILE_FAIL CMEMAKEHR(0X020) //檔案寫入失敗
//陣列操作
#define CM_ARRAY_BEYOND CMEMAKEHR(0X060) //陣列越界

//函式操作
#define CM_PARAM_BEYOND CMEMAKEHR(0X030) //引數越界

//操作
#define CM_OPEN_DATABASE_FAIL CMEMAKEHR(0X040) //資料庫開啟失敗
#define CM_OPEN_TABLE_FAIL CMEMAKEHR(0X041) //資料表開啟失敗
#define CM_TABLE_BEYOND CMEMAKEHR(0X042) //資料表訪問越界
#define CM_CREATE_DSN_FAIL CMEMAKEHR(0X043) //建立資料來源失敗
#define CM_TABLE_EXIST CMEMAKEHR(0X044) //表已經存在
#define CM_DATABASE_NOT_OPEN CMEMAKEHR(0X045) //資料庫沒有開啟


//其他操作
#define CM_BEYOND_PARAM CMEMAKEHR(0x100) //引數越界
#define CM_POINT_IS_INVALIAD CMEMAKEHR(0x101) //點為非法的點

//圖象操作
#define CM_CREATE_BMP_FAIL CMEMAKEHR(0x200) //建立點陣圖失敗

//執行緒操作
#define CM_THREAD_IS_LOCK CMEMAKEHR(0x300) //執行緒背鎖住


//數學計算
#define CM_EDGENUM_NOTENOUGH CMEMAKEHR(0x500) //邊數不夠

//操作
#define CM_CONNECT_FAIL CMEMAKEHR(0x700) //連線失敗

//三維裝置操作
#define DD_CREATE_DEVICE_FAIL CMEMAKEHR(0x400) //建立裝置失敗
#define DD_DELETE_DEVICE_FAIL CMEMAKEHR(0x401) //刪除裝置失敗
#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,"記憶體分配失敗","Memeroy"},
 {CM_MEM_FREE_FAIL,"記憶體釋放失敗","Memeroy"},
 {CM_INVALID_POINTER,"無效的指標","Memeroy"},
 {CM_CREATE_FILE_FAIL,"記憶體分配失敗","Memeroy"},
 {CM_OPEN_FILE_FAIL,"檔案開啟失敗","Memeroy"},
 {CM_CLOSE_FILE_FAIL,"檔案關閉失敗","Memeroy"},
 {CM_FILE_HAS_EXISTED,"檔案已經存在","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,"陣列越界","Memeroy"},
 {CM_PARAM_BEYOND,"引數越界","Memeroy"},
 {CM_OPEN_DATABASE_FAIL,"資料庫開啟失敗","Memeroy"},
 {CM_OPEN_TABLE_FAIL,"資料表開啟失敗","Memeroy"},
 {CM_TABLE_BEYOND,"資料表訪問越界","Memeroy"},
 {CM_CREATE_DSN_FAIL,"建立資料來源失敗","Memeroy"},
 {CM_TABLE_EXIST,"表已經存在","Memeroy"},
 {CM_DATABASE_NOT_OPEN,"資料庫沒有開啟","Memeroy"},
 {CM_CREATE_BMP_FAIL,"建立點陣圖失敗","Memeroy"},
 {CM_THREAD_IS_LOCK,"執行緒背鎖住","Memeroy"},
 {CM_POINT_IS_INVALIAD,"點為非法的點",""},
 {DD_CREATE_DEVICE_FAIL,"建立裝置失敗",""},
 {DD_DELETE_DEVICE_FAIL,"刪除裝置失敗",""}
};

static char * GetChineseMessage(HRESULT lResult)
{
 for(long lI=0;lI {
 if(sResultInfo[lI].lResultNo==lResult)
 {
 return sResultInfo[lI].szResultMessageChinese;
 }
 }
 return NULL;
}

static char * GetEnglishMessage(HRESULT lResult)
{
 for(long lI=0;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__


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-991790/,如需轉載,請註明出處,否則將追究法律責任。

相關文章