Windows客户端C/C++编程规范“建议”——函数
1 函數
1.1 代碼行數控制在80行及以內
等級:【要求】
說明:每個函數的代碼行數控制應該控制在80行以內。如果超過這個限制函數內部邏輯一般可以拆分。如果試圖超過這個標準,請列出理由。但理由不包含如下:
- 無法拆分。
- 流程內部邏輯復雜,無需拆分,即使拆分了,拆分的函數也不會被其他地方用到。(解釋:拆分可以減少代碼行數,提煉后的函數可以方便讀者快速理解函數邏輯并定位問題。)
1.2 代碼列數控制在100字符及以內
等級: 【要求】說明:每行代碼不可以超過100字符。如果超過這個字符數,代碼的美觀度和可閱讀性將降低。
? ? ? ? 例子:?
for ( std::vector<ATL::CString, COneClass>::iterator it = m_VecObjects.begin(); it != m_VecObjects.end(); it++ )
? ? ? ? 這一行一共113列。
? ? ? ? 我們可以這么修改:
typedef std::vector<ATL::CString, COneClass>::iterator VecCStClsIter;
for ( VecCStClsIter it = m_VecObjects.begin(); it != m_VecObjects.end(); it++ )
? ? ? ? 或者使用boost/C++11中的auto:
for ( auto it = m_VecObjects.begin(); it != m_VecObjects.end(); it++ )
1.3 避免重復代碼
等級:【要求】
說明:如果邏輯中重復代碼行數超過30行,應該考慮將該邏輯提煉成一個函數。這樣既可以增強代碼可讀性,還可以降低未來代碼維護的代價。
1.4 函數名稱不可以全大寫
等級: 【必須】說明:在“1.6宏”規則中,我們已經規定宏要使用全大寫方式定義。所以為了區分宏和函數,函數名不可以使用全大寫。
1.5 當函數不需要返回值時不要為其設計返回值
等級: 【要求】說明:如果給不需要返回值的函數設計返回值,將為使用該函數的人帶來困惑。
1.6 對于有返回值的函數要求每個退出分支都要有顯示的返回值
等級: 【必須】說明:對于有返回值的函數,如果邏輯進入一個沒有返回值的分支,將導致未知錯誤。
1.7 大內存數據參數需要使用引用傳遞
等級: 【要求】說明:如果不使用引用傳遞,則在函數調用時產生內存拷貝行為。大幅降低函數執行效率。
1.8 不會被改變的引用傳遞入參使用const聲明
等級: 【要求】說明:避免函數中對入參修改導致邏輯出錯。
1.9 入參先于出參排列
等級: 【要求】說明:這樣安排一般復合理解的需要。實際上很多Windows API也是基于這樣的規則設計的。
1.10 默認參數在函數定義時(非聲明)使用注釋標記默認值
等級:【推薦】
說明:這樣將在聲明定義分離的模式下,閱讀者可以快速知道該函數存在默認參數的情況。
void print( int nValue = 1 );
.......
void print( int nValue /*= 1*/ ) {printf("%d", nValue);
}
1.11 謹慎使用需要64位變量函數
等級:【要求】
說明:系統中很多需要64位變量的API存在于vista以上的系統中。如果我們代碼中使用該類函數,將導致在XP系統上運行出錯(當然可以動態加載系統dll并尋址以解決該問題)。
1.12 禁止使用非安全函數
等級: 【必須】說明:以前一批老的C函數存在不安全隱患。為了提高程序的健壯性,需使用安全版函數替代。?
? ? ? ? 比較常見的非安全函數:
| wcsncpy | strcpy | strncpy | memcpy |
| wmemcpy | sprintf | wprintf | vsnprintf |
? ? ? ? 編譯器報:
warning C4996: 'XXXX': This function or variable may be unsafe. Consider using wcsncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
? ? ? ? 如果是因為我們使用strsafe.h導致VC庫或者可信的第三方庫(比如boost)報該warning。則我們可以調整strsafe.h的包含位置等方法去除。
? ? ? ? 其他場景出現該warning,應該使用安全函數替代。這些函數的安全版本一般是在原函數后面增加_s。并新增一個空間大小的參數。
? ? ? ? 使用這些不安全函數存在以下危害:
- 產生臟數據。我們可能聲明一個變量為1,但是經過運行后,在沒有執行修改該變量的情況下,可能數據已經變成一個我們無法預計的值了。見下例n的輸出。
- 進入錯誤邏輯。因為棧空間被破壞,我們的邏輯可能進入并非我們希望進入的函數內部執行。
- 導致崩潰。因為溢出會導致堆棧被破壞,所以極可能導致程序崩潰。由于我們棧被破壞,導致棧回溯產生錯誤,將嚴重影響我們dump的分析。
- 被攻擊。緩沖區溢出攻擊,這種攻擊方式已經非常古老了。很多漏洞都是這種錯誤導致的。
? ? ? ? 簡單的例子:
#define UNSAFEint _tmain(int argc, _TCHAR* argv[])
{int m = -1;char buffer[8] = {0};int n = -1;std::string str = "012345678901234567890123456789";
#ifdef UNSAFEmemcpy(buffer, str.c_str(), str.length());
#elsememcpy_s(buffer, _countof(buffer), str.c_str(), str.length());
#endifprintf("%d %d\n", m, n);return 0;
}? ? ? ? 在release無優化的情況下,該段會產生臟數據并崩潰。(n的值已經被改變)1.13 不要寄希望于inline聲明
等級:【必須】
說明:VS平臺上一個被聲明為inline的函數并不一定會被內嵌到代碼中,而是和普通函數一樣。因為VS會依據自己的規則判斷是否需要做真實的“內聯”。(轉載請指明出于breaksoftware的csdn博客)
總結
以上是生活随笔為你收集整理的Windows客户端C/C++编程规范“建议”——函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows客户端C/C++编程规范“
- 下一篇: Windows客户端C/C++编程规范“