C/C++ 编程规范总结-2
https://developer.aliyun.com/article/1507971
5. 变量、常量与类型
规则5-1
宏定义中如果包含表达式或变量,表达式和变量必须用小括号括起来。
宏定义中,对表达式和变量使用括号,可以避免可能发生的计算错误
正例:
#define HANDLE(A,B) (( A ) / ( B ))
反例:
#define HANDLE(A,B) (A / B)
规则5-2
使用宏定义多行语句时,必须使用把这些语句括起来。
在宏定义中,对多行语句使用大括号,可以避免可能发生的错误。
建议5-3
结构中元素的个数应适中。
若结构中元素个数过多可考虑依据某种原则把元素组成不同的子结构,以减少原结构中元素的个数
6. 表达式与语句
规则6-1
一条语句只完成一个功能
规则6-2
在表达式中使用括号,使表达式的运算顺序更清晰。
由于将运算符的优先级与结合律熟记是比较困难的,为了防止产生歧义并提高可读性,即使不加括号时运算顺序不会改变,也应当用括号确定表达式的操作顺序。
规则6-3
避免表达式中的附加功能,不要编写太复杂的复合表达式。
规则6-4
不可将布尔变量和逻辑表达式直接与TRUEFALSE或者1、0进行比较。TURE和FALSE的定义值是和语言环境相关的,且可能会被重定义的
规则6-5
在条件判断语句中,当整型变量与0比较时,不可模仿布尔变量的风格,应当将整型变量用“==”或“!=”直接与0比较。
规则6-6
不可将浮点变量用“==”或“!=”上任何数字比较
规则6-7
应当将指针变量用“==”或“!=”与NULL比较
规则6-8
在switch语句中,每一个case分支必须使用break结尾,最后一个分支必须是default分支。
规则6-9
不可在for 循环体内修改循环变量防止for 循环失去控制。
建议6-10
循环嵌套次数不大于3次
7. 函数与过程
规则7-1
如果函数没有参数,则用void填充
规则7-2
如果参数是指针,且仅作输入用,则应在类型前加const。
防止该指针在函数体内被意外修改
例如:
int GetStrLen(const char *pcString):
规则7-3
不要省略返回值的类型,如果函数没有返回值,那么应声明为void类型
- C语言中,凡不加类型说明的函数,一律自动按整型处理。如果不注明类型,容易被误解为void类型,产生不必要的麻烦
- C++语言有很严格的类型安全检查,不允许上述情况发生。由于C++程序可以调用C函数,为了避免混乱,规定任何C/ C++函数都必须有类型。
规则7-4
对于有返回值的函数,每一个分支都必须有返回值。
为了保证对被调用函数返回值的判断,有返回值的函数中的每一个退出点都需要有返回值。
规则7-5
防止将函数的参数作为工作变量
将函数的参数作为工作变量,有可能错误地改变参数内容,所以很危险。对必须改变的参数最好先用局部变量代之,最后再将该局部变量的内容赋给该参数。
建议7-6
如果返回值表示函数运行是否正常规定0为正常退出,不同非0值标识不同异常退出。避免使用TRUE或FALSE作为返回值
建议7-7
函数体的规模不能太大,尽量控制在200行代码之内
建议7-8
减少函数本身或函数间的递归调用
递归调用特别是函数间的递归调用 (如A->B->C->A),影响程序的可理解性,递归调用一般都占用较多的系统资源(如栈空间) ;递归调用对程序的测试有一定影响。故除非为某些算法或功能的实现方便,应减少没必要的递归调用。
对于前台软件为了系统的稳定性和可靠性,往往规定了进程的堆栈大小。如果采用了递归算法,收敛的条件又往往难以确定,很容易使得进程的堆栈溢出,破坏系统的正常运行:另外,由于无法确定递归的次数,降低了系统的稳定性和可靠性。
建议7-9
设计高扇入、合理扇出的函数
扇出是指一个函数直接调用(控制) 其它函数的数目,而扇入是指有多少上级函数调用它。
扇出过大,表明函数过分复杂,需要控制和协调过多的下级函数:而扇出过小,如总是1,表明函数的调用层次可能过多,这样不利于程序阅读和函数结构的分析,并且程序运行时会对系统资源如堆栈空间等造成压力。函数较合理的扇出(调度函数除外) 通常是3-5。扇出太大,一般是由于缺乏中间层次,可适当增加中间层次的函数。扇出太小,可把下级函数进步分解成多个函数,或合并到上级函数中。当然分解或合并函数时,不能改变要实现的功能,也不能违背函数间的独立性。
扇入越大,表明使用此函数的上级函数越多,这样的函数使用效率高,但不能违背函数间的独立性而单纯地追求高扇入。公共模块中的函数及底层函数应该有较高的扇入。
8. 可靠性
规则8-1
在程序编制之前,必须了解编译系统的内存分配方式,特别是编译系统对不同类型的变量的内存分配规则,如局部变量在何处分配、静态变量在何处分配等。
规则8-2
防止内存操作越界
内存操作主要是指对数组、指针、内存地址等的操作,内存操作越界是软件系统主要错误之一,后果往往非常严重,所以当我们进行这些操作时一定要仔细。
规则8-3
必须对动态申请的内存做有效性检查,并进行初始化;动态内存的释放必须和分配成对以防止内存泄漏,释放后内存指针置为NULL。
- 对嵌入式系统,通常内存是有限的,内存的申请可能会失败,如果不检查就对该指针进行操作,可能出现异常,而且这种异常不是每次都出现比较难定位。
- 指针释放后,该指针可能还是指向原有的内存块,可能不是,变成个野指针,一般用户不会对它再操作,但用户失误情况下对它的操作可能导致程序崩溃。
规则8-4
不使用realloc()。
调用realloc对一个内存块进行扩展,导致原来的内容发生了存储位置的变化, realloc函数既要调用free,又要调用malloc。执行时究竟调用哪个函数,取决于是要缩小还是扩大相应内存块的大小
规则8-5
变量在使用前应初始化,防止未经初始化的变量被引用。
不同的编译系统,定义的变量在初始化前其值是不确定的。有些系统会初始化为0,而有些不是
规则8-6
指针类型变量必须初始化为NULL
规则8-7
指针不要进行复杂的逻辑或算术操作。
指针加一的偏移,通常由指针的类型确定,如果通过复杂的逻辑或算术操作,则指针的位置就很难确定
规则8-8
如果指针类型明确不会改变,应该强制为const类型的指针,以加强编译器的检查
可以防止不必要的类型转换错误。
建议8-9
C++程序中,分配内存使用new和delete,而不使用malloc和free。
new和delete操作由编译器决定具体分配和释放内存的大小,相对于malloc和firee更为高级
9. 可测试性
规则9-1
在同一项目组或产品组内,为准备集成测试和系统联调,要有一套统一的调测开关及相应信息输出函数,并且要有详细的说明统一的调试接口和输出函数由模块设计和测试人员根据项目特性统一制订,由项目系统人员统一纳入系统设计中
规则9-2
在同一个项目组或产品组内,调测打印出的信息串要有统一的格式。信息串中应当包含所在的模块名(或源文件名) 及行号等信息。
规则9-3
在编写代码之前,应预先设计好程序调试与测试的方法和手段,并设计好各种调测开关及相应测试代码(如打印函数等)
程序的调试与测试是软件生存周期中非常重要的一个阶段,如何对软件进行较全面、高效率的测试并尽可能地找出软件中的错误就成为非常关键的问题。因此在编写源代码之前,除了要有一套比较完善的测试计划外,还应设计出一系列测试代码作为手段,为单元测试、集成测试及系统联调提供方便
10.断言与错误外理
规则10-1
整个软件系统应该采用统一的断言。如果系统不提供断言,则应该自己构造一个统一的断言供编程时使用。
规则10-2
指向指针的指针及更多级的指针必须逐级检查
规则10-3
正式软件产品中应把断言及其它调测代码去掉 (即把有关的调测开关关掉 ) ,加快软件运行速度。