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. 产生脏数据。我们可能声明一个变量为1,但是经过运行后,在没有执行修改该变量的情况下,可能数据已经变成一个我们无法预计的值了。见下例n的输出。
  2. 进入错误逻辑。因为栈空间被破坏,我们的逻辑可能进入并非我们希望进入的函数内部执行。
  3. 导致崩溃。因为溢出会导致堆栈被破坏,所以极可能导致程序崩溃。由于我们栈被破坏,导致栈回溯产生错误,将严重影响我们dump的分析。
  4. 被攻击。缓冲区溢出攻击,这种攻击方式已经非常古老了。很多漏洞都是这种错误导致的。

        简单的例子:

#define UNSAFE

int _tmain(int argc, _TCHAR* argv[])
{
	int m = -1;
	char buffer[8] = {0};
	int n = -1;
	std::string str = "012345678901234567890123456789";
#ifdef UNSAFE
	memcpy(buffer, str.c_str(), str.length()); 
#else
	memcpy_s(buffer, _countof(buffer), str.c_str(), str.length()); 
#endif
	printf("%d %d\n", m, n);
	return 0;
}
        在release无优化的情况下,该段会产生脏数据并崩溃。(n的值已经被改变)


1.13 不要寄希望于inline声明

等级:【必须】

说明:VS平台上一个被声明为inline的函数并不一定会被内嵌到代码中,而是和普通函数一样。因为VS会依据自己的规则判断是否需要做真实的“内联”。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。