總結(jié)一些日常工作常見的代碼和編程情況昔驱,會分幾個文章寫出疹尾,也許對一些朋友有些作用
一、基礎(chǔ)風(fēng)格
1舍悯、設(shè)計函數(shù)參數(shù)時航棱,請注意函數(shù)參數(shù)的順序和個數(shù)
????*一般而言,建議輸入?yún)?shù)在前萌衬,輸出參數(shù)在后
????*輸入?yún)?shù)是指針饮醇,且不改變時,請注意定義的時候類型加const
????*輸入?yún)?shù)是struct或者Class實(shí)例且不修改時秕豫,可以使用const&來傳遞朴艰,或者const指針
????????**這樣可以避免當(dāng)傳入形參的實(shí)例對象較大時观蓄,拷貝會降低效率
????????**還可以避免對該對象的改變
????*盡可能的減少不必要的參數(shù)個數(shù)
2、避免不必要的函數(shù)調(diào)用
????*不合理的代碼:
????char* pszString = “hello祠墅, it's me”;
????for(int i = 0;i < strlen(pszString); ++i)
????{
????????侮穿。。毁嗦。DoSomething
????}
????*合理的代碼
????char* pszString = “hello亲茅, it's me”;
????int nLength = (int)strlen(pszString);
????for(int i = 0;i < nLength; ++i)
????{
????????。狗准。克锣。DoSomething
????}
3、避免頭文件定義全局變量腔长,防止重復(fù)引用頭文件時袭祟,全局變量被重復(fù)定義;
4捞附、字符串拼接時請注意避免越界
????1)因?yàn)橛袝r候文件存儲的文件名字段可能是錯的巾乳,避免異常情況下的越界,拼接時需要注意是否越界
????2)snprintfs的count參數(shù)請用_TRUNCATE鸟召,避免buffer太小導(dǎo)致的宕機(jī)(針對windows的PC)
????https://msdn.microsoft.com/en-us/library/ms175769.aspx
5胆绊、如果處理資源出錯,需要寫日志時药版,請寫明出錯的文件名
6辑舷、繼承接口或抽象類的子類,析構(gòu)函數(shù)請注意定義為virtual
????*當(dāng)指針類型是父類指針槽片,指向的內(nèi)容是子類對象時,定義為virtual可以避免delete該指針時會出現(xiàn)泄露的情況
7肢础、DWORD/long和void*轉(zhuǎn)換
????*64位的程序下还栓,void*是8字節(jié),DWORD/long是4字節(jié)传轰。這樣如果是指針存儲在DWORD剩盒,高位的4個字節(jié)是會丟失
8、推薦調(diào)用函數(shù)時慨蛙,對返回值進(jìn)行判斷辽聊,同時對錯誤的返回值做相應(yīng)的錯誤處理
????*確認(rèn)是否需要函數(shù)返回值?
????*確認(rèn)出錯了怎么辦期贫?錯誤如何表達(dá)跟匆?
????*是否需要定義出錯處理的準(zhǔn)則或出錯后是否需要還原?
二通砍、斷言的使用
1玛臂、static_assert執(zhí)行編譯時的斷言檢查
????如:static_assert(sizeof(char *) == 8, "不是64位模式")
2烤蜕、斷言來檢查參數(shù)合法性的場景:
????1)代碼執(zhí)行之前或者在函數(shù)入口處,用斷言來檢查參數(shù)的合法性
????2)代碼執(zhí)行之后或在函數(shù)出口處迹冤,用斷言來檢查是否正確執(zhí)行讽营,輸出或內(nèi)部狀態(tài)是否如預(yù)期
3、避免用斷言去檢查程序錯誤:
????1)外部不可靠的數(shù)據(jù)泡徙,應(yīng)該做嚴(yán)格檢查才能放到系統(tǒng)內(nèi)部橱鹏,這個時候它是守衛(wèi),提前檢驗(yàn)和過濾不合理的數(shù)據(jù)和參數(shù)堪藐,應(yīng)該使用錯誤檢查處理代碼莉兰,而不是用斷言來做檢查
????????*外部不可靠的數(shù)據(jù):如不合理的用戶輸入、或其他模塊傳入到該模塊的消息或數(shù)據(jù)庶橱,
????????有點(diǎn)像是對項(xiàng)目組外的員工贮勃,要動用項(xiàng)目組的資源,是需要經(jīng)過審核的
????2)系統(tǒng)內(nèi)部的交互數(shù)據(jù)(如程序內(nèi)部的調(diào)用)苏章,可以用斷言來檢查意想不到的錯誤寂嘉,或者程序內(nèi)部的假設(shè)。(其實(shí)就是檢查它的潛規(guī)則和邏輯邊界枫绅、隱形假定)
????????*因?yàn)橄到y(tǒng)內(nèi)的調(diào)用者一般情況下是有義務(wù)負(fù)責(zé)傳遞給自己內(nèi)部的數(shù)據(jù)是合理正常的數(shù)據(jù)
????????就像項(xiàng)目內(nèi)的員工泉孩,對于使用項(xiàng)目組的資源的門檻就會低很多
????????*可理解為assert和出錯處理是對所寫程序建立不同的信用級別,也方便在Release版可以性能更好的運(yùn)行程序
????3)推薦針對public的函數(shù)或接口的入口處對參數(shù)做嚴(yán)格的錯誤檢查并淋;對于Private的函數(shù)或者只有這個模塊能看到的寓搬,可以用assert
4、避免斷言中使用改變環(huán)境的語句:
????如不正確的代碼:
????int Test(int i)
????{
????????assert(i++); ?//debug版和release版的i值就會不一樣
????????return i;
????}
????int main()
????{
????????int i = 1;
????????int nValue = Test(i);
????????printf("%d\n", ?nValue);
????????return 0;
????}
????合理的形式:
????int Test(int i)
????{
????????assert(i);
????????return ++i;
????}
????注:與改變環(huán)境的語句類似的行為是宏定義县耽。
????*請盡量不要在assert中調(diào)用宏句喷,以防止宏的副作用
5、防錯性程序設(shè)計常常要解決的是:現(xiàn)實(shí)中兔毙,防止用戶數(shù)據(jù)丟失或程序崩潰而采取的措施唾琼。只是除此之外:
????*我們是否還希望進(jìn)行防錯性程序設(shè)計時,錯誤不要被隱瞞澎剥?
????*實(shí)現(xiàn)程序時锡溯,我們想要什么?我們期待錯誤發(fā)生時哑姚,我們能得到什么或者怎樣處理它祭饭?
????????**這有點(diǎn)像我們接到一個任務(wù)的思考點(diǎn):
????????1)我們做這個事的目標(biāo)是完成它?做好它叙量?
????????2)做這個事是希望減輕自己負(fù)擔(dān)倡蝙?減輕別人負(fù)擔(dān)?or both宛乃?
????????3)出問題時怎么辦悠咱?有預(yù)案嗎蒸辆?有線索嗎?系統(tǒng)里需要埋下什么報警方案析既?影響大嗎躬贡?
三、內(nèi)存
1眼坏、內(nèi)存常見的設(shè)計問題導(dǎo)致的代碼bug
????1)內(nèi)存的分配拂玻、釋放接口應(yīng)配對;且應(yīng)該限定在一個同一模塊或者同一抽象層內(nèi)進(jìn)行
????????*否則會加大程序猿追蹤內(nèi)存塊的生命周期的負(fù)擔(dān)
????????*還可能導(dǎo)致內(nèi)存泄露宰译、重復(fù)釋放該內(nèi)存檐蚜、非法訪問已經(jīng)釋放的內(nèi)存、寫入已經(jīng)釋放或者沒有分配的內(nèi)存等問題
????例:
????**不正確的代碼:
????#define MIN_MEM_SIZE 10
????int CompareMemorySize(char* pchBuffer, size_t uSize)
????{
????????if(uSize < MIN_MEM_SIZE)
????????{
????????????free(p);
????????????p = NULL;
????????????return -1;
????????}
????????return 0;
????}
????void AllocMemory(size_t uSize)
????{
????char *pchBuffer = (char*)malloc(uSize);
????if(pchBuffer == NULL)
????{.........}
????if(CompareMemorySize(pchBuffer, uSize) == -1) ?//這里pchBuffer實(shí)際上是已經(jīng)釋放了
????{
????????free(pchBuffer);//重復(fù)釋放了pchBuffer
????????pchBuffer = NULL;
????????return;
` ? }
????........
????free(pchBuffer);
????pchBuffer = NULL;
}
**正確的代碼:
#define MIN_MEM_SIZE 10
int CompareMemorySize(char* pchBuffer, size_t uSize)
{
????if(uSize < MIN_MEM_SIZE)
????{
????????return -1;
????}
????return 0;
}
void AllocMemory(size_t uSize)
{
????char *pchBuffer = (char*)malloc(uSize);
????if(pchBuffer == NULL)
????{.........}
????if(CompareMemorySize(pchBuffer, uSize) == -1)
????{
????????free(pchBuffer);
????????pchBuffer = NULL;
????????return;
` }
????????........
????free(pchBuffer);
????pchBuffer = NULL;
}
????**這里可以總結(jié)出的維度:
(1)內(nèi)存的分配沿侈、釋放接口應(yīng)配對闯第;且應(yīng)該限定在一個同一模塊或者同一抽象層內(nèi)進(jìn)行
(2)盡可能函數(shù)的副作用越少越好:減少用戶記住調(diào)用函數(shù)時,需要了解過多的函數(shù)內(nèi)部的實(shí)現(xiàn)
2缀拭、避免對結(jié)構(gòu)體執(zhí)行逐個字節(jié)的比較
如:
typedef struct
{
char chValue;
int nCount;
char szBuffer[10];
}Buffer;
*不合理的代碼:
bool IsEqual(const Buffer *pBuffer1, const Buffer *pBuffer2)
?{
????//因?yàn)槟J(rèn)情況想存在字節(jié)對齊咳短,對齊的字節(jié)內(nèi)存里填充什么內(nèi)容和編譯器有關(guān)系,它的行為未定義蛛淋,所以這樣比較是錯的
????if(!memcmp(pBuffer1, pBuffer2, sizeof(Buffer)))
????????return true;
????return false;
}
*合理的代碼:
bool IsEqual(const Buffer *pBuffer1, const Buffer *pBuffer2)
{
????if(
????????pBuffer1->chValue == pBuffer2->chValue &&
????????pBuffer1->nCount == pBuffer2->nCount &&
` ? ? strcmp(pBuffer1->szBuffer, pBuffer2->szBuffer) == 0
????)
????????return true;
????return false;
}
3咙好、避免執(zhí)行零長度的內(nèi)存分配
????*C99規(guī)定,程序試圖調(diào)用malloc褐荷、calloc與realloc等系列內(nèi)存分配函數(shù)分配大小為0的內(nèi)存勾效,其行為時有具體編譯器所定義(可能返回NULL,可能返回長度為非零的值等)叛甫,從而導(dǎo)致產(chǎn)生未定義的行為
4层宫、內(nèi)存常見檢測問題:
????1)確保沒有訪問空指針
????2)分配和釋放內(nèi)存的操作是否配對
????3)出錯處理時,內(nèi)存是否有出現(xiàn)重復(fù)釋放其监、漏釋放卒密、或者釋放錯誤
????4)在指針賦值前,是否存在有內(nèi)存數(shù)據(jù)丟失
????5)當(dāng)釋放數(shù)組元素或者struct類型的元素時棠赛,都應(yīng)先遍歷子元素進(jìn)行釋放,再遍歷回父節(jié)點(diǎn)
????*注:如果class或者一些設(shè)計不好的接口膛腐,沒有注意提供配對的內(nèi)存操作接口時睛约,會導(dǎo)致各種隱患,如:內(nèi)存泄漏哲身、野指針等辩涝,需要另外檢查
????6)注意對函數(shù)返回值是動態(tài)分配內(nèi)存的檢查和內(nèi)部狀態(tài)的檢查
????7)引用計數(shù)的檢查
????8)內(nèi)存填充是否越界、或數(shù)據(jù)類型不正確