代码编写规范说明
V0.1
Date: 2020/09
说明:
编码规范鼓励大家遵循
Google c++
编码规范。
鉴于
Google
编码规范内容过多,在此基础上提出部分重点信息在此文档说
明,重点部分务必遵守,对自己有严苛要求的,尽可能多的遵守
Google c++
编码
规范
.
排版布局
排版布局应做到美观大方;
1.1
合理使用空行:
1.1.1
函数体之间用空行分隔。
1.1.2
结构
/
联合
/
枚举声明用空行分隔。
1.1.3
不同功能的代码块之间用空行分隔。
1.1.4
将功能类似的代码(如宏定义、类型定义、函数声明和全局变量)放在一
起,和其他部分用空行分隔。
1.1.5
使用空行时,一行就够了,不要连续使用多个空行,那样会让人感觉代码
空荡荡的。
1.2
合理使用空格
1.2.1
等号两边使用空格。
如:
int a = 100;
深圳乐生机器人
Co., Ltd.
1.2.2
参数之间使用空格。
如:
test(int a, int b, int c)
1.2.3
语句末的分号与前面内容不要加空格。
如:
test(a, b, c);
1.2.4
其他能让代码更美观的地方。
1.3
合理使用括号
1.3.1
用括号分隔子表达式,不要只靠默认优先级来判断。
如:
( (a && b) || (c && d) )
1.3.2
用括号分隔
if/while/for
等语句的代码块,哪怕代码块只有一行。
如:
if(a > b)
{
return c;
}
1.4
合理的缩进方式
每一级都正常缩进,用
tab
缩进取代空格缩进。之前用空格缩进的目的是防
止代码因编辑器的
tab
宽度不同而变乱,现在这个担心是多余的了,代码编辑器
都支持
tab
宽度设置了(统一设置为
4
个空格)。如果代码缩进的层次太多
(
比如
超过三层
)
,则可能是代码设计上出了问题。(
google
规范中部分
tab
宽度设置为
2
,这里我们统一设置为
4
,访问限定符保持和
google
规范一致,缩进为
1
)
如:
if(a > b)
{
for(int i = 0; i < 100; i++)
{
…
}
}
命名和注释:
命名约定
1
、总体规则:不要随意缩写,如果说
ChangeLocalValue
写作
ChgLocVal
还有情
可原的话,把
ModifyPlayerName
写作
MdfPlyNm
就太过分了,除函数名可适当
为动词外,其他命名尽量使用清晰易懂的名词;
2
、宏、枚举等使用全部大写
+
下划线;
3
、变量(含类、结构体成员变量)、文件、命名空间、存取函数
(
指类成员变量
存取
)
等使用全部小写
+
下划线,类成员变量以下划线结尾,全局变量以
g_
开头;
4
、普通函数、类型(含类与结构体、枚举类型)、常量等使用大小写混合,不含
下划线;
使用这套命名约定,可以使代码具有一定程度的“自注释”功能,方便他人
阅读,也方便自己以后修改。
注释:
首先,尽可能多的遵循约定好的代码编写规范,就是最好的注释
;
有意义的
类型名和变量名
,
要远胜过要用注释解释的含糊不清的名字
.
隐晦难懂的变量声明和定义、宏定义、常量,全局变量等,添加注释;
对外提供的函数接口、内部函数实现特殊的操作等添加注释;
参考
Google
规范添加。
从源码编写开始,避免容易犯的错误
内存泄漏:
在
C/C++
中,内存管理器不会自动回收不再使用的内存,如果你忘了释放不
再使用的内存,这些内存就不能被重用了,这就造成了内存泄漏;
malloc
和
free
应配对使用,
new
要和
delete/delete[]
配对使用。重载了类的
new
操作,应该同时重载类的
delete/delete[]
操作。
内存越界访问
内存越界访问有两种,一种是读越界,即读了不属于自己的数据。如果所读
的内存地址是无效的,程序立刻就崩溃了;如果所读内存地址是有效的,在读的
时候不会马上出现问题,但是由于读到的数据是随机的,因此它会造成不可预料
的后果。另外一种是写越界,所写入的数据对别的程序来说是随机的,它会造成
不可预料的后果。
内存越界访问会造成非常严重的后果,是程序稳定性的致命威胁之一。更麻
烦的是,它出现的时机是随机的,表现出来的症状是随机的,而且造成的后果也
是随机的。
这里举两个例子:
1
,数据访问越界,访问数组数据时,未注意数组元素的大小,导致访问越
界。
2
,
strcpy(str, argv[1]);
这里是否存在越界错误依赖于外部输入的数据,这样的
写法在接受正常输入的情况下可能没有问题,但只要受到一点恶意攻击
就,或者调用者不注意,就存在了越界的情况了。所以,除非你确定输
入的数据是在你的控制之内的,否则不要用
strcpy,strcat
和
sprintf
之类的
函数,使用
strncpy
、
strncat
和
snprintf
代替。
遗留野指针,访问空指针
野指针指的是那些指向你已经释放掉的内存的指针。释放掉的内存会被内存
管理器重新分配,此时,野指针指向的内存已经被赋予新的意义。对野指针的访
问,无论是有意的还是无意的,都可能会带来巨大的代价,因为它造成的后果,
如同越界访问一样是不可预料的。
释放内存后应当立即把对应的指针置为空值。需要注意:如果指针是从函数
外层传入的,那么就算在函数内把指针置为空值,也不会对外层的指针造成任何
访问空指针,空指针在
C/C++
中有特殊的地址,通常它是用来判断一个指针
的有效性。空指针一般定义为
0
。现代操作系统都会保留从
0
开始的一块内存,
一旦程序试图访问这块内存,系统就会触发一个异常信号。
返回指向临时变量的指针
大家都知道,栈里面的变量都是临时的。当前函数执行完成时,相关的临时
变量和参数就都被清除了。不能把指向这些临时变量的指针返回给调用者,因为
这样的指针指向的数据是随机的,会给程序造成不可预料的后果。
误解传值与传引用
在
C/C++
中,参数默认传递方式是传值的,即在参数入栈时被复制一份。在
函数里修改这些参数,不会影响外面的调用者。因此在操作函数传递的参数时,
务必谨记这些值只是临时拷贝数据而已。
指针操作时由于概念模糊导致的错误
比如:
int *p = ….; p + 1
等于
(size_t)p + 1
吗?
事实上
p + n
等于
(size_t)p + n * sizeof(*p)
。
应谨记对指针的操作,跟指针指向的数据类型密切相关。
结构体赋值不当引发的错误
在初始化一个结构体时,应避免使用下面这种方式对结构体赋值:
struct s s1 = {4, “abcd”};
正确的初始化方法应该是:
struct s s1 = {.l = 4, .p = “abcd”};
引用未初始化的变量
未初始化变量的内容是随机的,使用这些数据会造成不可预料的后果,调试
这样的
bug
也是非常困难的。要防止这类
bug
出现其实非常容易,只需在声明变
量时记得对它进行初始化就行。另外我们也要重视编译器的警告信息,一旦发现
引用了未初始化的变量,就要立即修改过来。
试图修改常量
避免修改全局常量和常量字符串,原因在于他们是放在
.rodata
里面,
而
.rodata
内存页面是不能修改的。试图对他们进行修改会引发内存错误。
符号重名
无论是函数名还是变量名,如果在不同的作用域内重名,自然没有问题。但
如果两个符号的作用域有交集,如全局变量和局部变量,全局变量与全局变量之
间,重名的现象一定要坚决避免。
栈溢出
每一个操作系统平台,堆与栈的空间大小设定不一样,通常定义一些不是特
别大的临时变量都不会有问题,如果要定义大的临时变量,应考虑栈溢出的错误,
采取必要措施避免栈溢出的可能;
误用
sizeof
尽管
C/C++
通常是按值传递参数,但数组是个例外,在传递数组参数时,数
组退化为指针
(
即按引用传递
)
,此时用
sizeof
是无法获取数据的大小的。
如:
void test(char str[20])
{
printf(“%s:size=%d\n”, __func__, sizeof(str));
}
int main(int argc, char* argv[])
{
char str[20] = {0};
test(str);
printf(“%s:size=%d\n”,__func__, sizeof(str));
深圳乐生机器人
Co., Ltd.
return 0;
}
字节对齐
字节对齐主要目的是提高内存访问的效率。但在有的平台
(
如
ARM7)
上,就不
光是效率问题了,如果不对齐,得到的数据都是错误的。
所幸的是,大多数情况下,编译器会保证全局变量和临时变量都能按正确的
方式对齐。内存管理器会保证动态内存按正确的方式对齐。要注意的是,在不同
类型的变量之间进行转换时一定要小心,如把
char *
强制转换为
int *
时,要格外
小心。
另外,字节对齐也会造成结构体大小的变化,如果是在程序内部,用
sizeof
来取得结构的大小就足够了。若数据要在不同的机器间传递时,就需要在通信协
议中规定对齐的方式,以避免对齐方式不一致引发的问题。
字节顺序
字节顺序历来是设计跨平台软件时头疼的问题。字节顺序是关于数据在物理
内存中的布局问题,最常见的字节顺序有两种:大端模式与小端模式。
在开发与网络通信和数据交换有关的软件时,要特别注意字节顺序问题。
多线程共享变量没有用
valotile
修饰
关键字
valotile
的作用时告诉编译器,不要把变量优化到寄存器里。在开发
多线程并发的软件时,如果这些线程共享一些全局变量,这些全局变量最好用
valotile
修饰。这样可以避免因为编译器优化而引起的错误
(
往往这样的错误非常
难查
)
遵循编程思想
高内聚(
High Cohesion
)
高内聚是一个普遍用来评判软件设计质量的标准。内聚,更为专业的说法叫功能内聚,是对
软件系统中元素职责相关性和集中度的度量。如果元素具有高度相关的职责,除了这些职责
内的任务,没有其它过多的工作,那么该元素就具有高内聚性,反之则为低内聚性。高内聚
要求软件系统中的各个元素具有较高的协作性,因为在我们在完成软件需求中的一个功能,
可能需要做各种事情,但是具有高内聚性的一个元素,只完成它职责内的事情,而把那些不
在它职责内的事情拿去请求别人来完成。
低耦合(
Low Coupling
)
耦合是对某元素与其它元素之间的连接、感知和依赖的量度。这里所说的元素,即可以是功
能、对象(类),也可以指系统、子系统、模块。假如一个元素
A
去连接元素
B
,或者通过
自己的方法可以感知
B
,或者当
B
不存在的时候就不能正常工作,那么就说元素
A
与元素
B
耦合。耦合带来的问题是,当元素
B
发生变更或不存在时,都将影响元素
A
的正常工作,
影响系统的可维护性和易变更性。同时元素
A
只能工作于元素
B
存在的环境中,这也降低
了元素
A
的可复用性。正因为耦合的种种弊端,我们在软件设计的时候努力追求
“
低耦合
”
。
低耦合就是要求在我们的软件系统中,某元素不要过度依赖于其它元素。
在实际的软件开发过程中,我们不应过度的追求高内聚、低耦合,强内聚和
弱耦合是相辅相成的,一个良好的设计是由若干个强内聚模块以弱耦合的方式组
装起来的。除了高内聚低耦合的原则,我们在开发过程中应该对可读性、复用性、
可维护性和易变更性方面投入更多的思考。