Tanky WooRSS

读《华为C++编程规范》笔记

20 Oct 2011
这篇博客是从旧博客 WordPress 迁移过来,内容可能存在转换异常。

首先给出我读的《华为C++编程规范》的下载传送门:

(等待上传ing...)

说明:以下都是我把我在看规范的过程中,觉得对“我”有用的地方摘录了下来,注意,我把“我”字强调了,说明我摘录的几点你们不一定觉得好。我建议大家都看看这份编码规范(网上还有google的,也建议看看),当然,什么东西都不是绝对完美的,就像这份规范一样,大家可以选择性的吸纳,规范加个性(自己的习惯),两者结合,把握好自己平衡点,才是好的。

在这几天,我也会抽时间把google的C++编码规范做一下笔记,也希望更多的朋友一起学习,一起交流。

(交流论坛:

C++奋斗乐园:www.CppLeYuan.com

)

附:最近在公司实习(今天刚辞了),给我最大的感受就是一个代码的规范性,以前写的都是几十行的小代码,所以无法感受到,真正看了大的项目,首先连一些基本(算是约定俗成的规范吧!),我都不知道,所以才额外关注了编码的规范问题!还有很好需要学的啊。。。

---------------以下是笔记--------------

-----|排版|-----

1.对齐只使用空格键,不使用TAB键(对应原文件1.8) 说明:以免用不同的编辑器阅读程序时,因TAB键所设置的空格数目不同而造成程序布局不整齐,不要使用BC作为编辑器合版本,因为BC会自动将8个空格变为一个TAB键,因此使用BC合入的版本大多会将缩进变乱。 (个人说明:这个以前没有注意,那天在公司,header首先给我的VS2008改了几个选项,就包括这个,当然有点不明白,现在懂了)

2.在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、之后或者前后要加空格;进行非对等操作时,如果是关系密切的立即操作符(如->),后不应加空格。(对应1.11) 说明:采用这种松散方式编写代码的目的是使代码更加清晰。 由于留空格所产生的清晰性是相对的,所以,在已经非常清晰的语句中没有必要再留空格,如果语句已足够清晰则括号内侧(即左括号后面和右括号前面)不需要加空格,多重括号间不必加空格,因为在C/C++语言中括号已经是最清晰的标志了。 在长语句中,如果需要加的空格非常多,那么应该保持整体清晰,而在局部不加空格。给操作符留空格时不要连续留两个以上空格。

示例:

(1) 逗号、分号只在后面加空格。
int a, b, c;  

(2)比较操作符, 赋值操作符"=" "+=",算术操作符"+""%",逻辑操作符"&&""&",位域操作符"<<""^"等双目操作符的前后加空格。
if (current_time >= MAX_TIME_VALUE) 
a = b + c;
a *= 2;
a = b ^ 2; 

(3)"!""~""++""--""&"(地址运算符)等单目操作符前后不加空格。
*p = 'a';        // 内容操作"*"与内容之间
flag = !isEmpty; // 非操作"!"与内容之间
p = &mem;        // 地址操作"&" 与内容之间
i++;             // "++","--"与内容之间

(4)"->""."前后不加空格。
p->id = pid;     // "->"指针前后不加空格

(5) ifforwhileswitch等与后面的括号间应加空格,使if等关键字更为突出、明显。
if (a >= b && c > d)

-----|注释|-----

3.一般情况下,源程序有效注释量必须在20%以上。(对应2.1) 说明:注释的原则是有助于对程序的阅读理解,在该加的地方都加了,注释不宜太多也不能太少,注释语言必须准确、易懂、简洁。

4.说明性文件(如头文件.h文件、.inc文件、.def文件、编译说明文件.cfg等)头部应进行注释,注释必须列出:版权说明、版本号、生成日期、作者、内容、功能、与其它文件的关系、修改日志等,头文件的注释中还应有函数功能简要说明。(对应2.2)

示例:下面这段头文件的头注释比较标准,当然,并不局限于此格式,但上述信息建议要包含在内。

/*************************************************
  Copyright (C), 1988-1999, Huawei Tech. Co., Ltd.
  File name:      // 文件名
  Author:       Version:        Date: // 作者、版本及完成日期
  Description:    // 用于详细说明此程序文件完成的主要功能,与其他模块
                  // 或函数的接口,输出值、取值范围、含义及参数间的控
                  // 制、顺序、独立或依赖等关系
  Others:         // 其它内容的说明
  Function List:  // 主要函数列表,每条记录应包括函数名及功能简要说明
    1. ....
  History:        // 修改历史记录列表,每条修改记录应包括修改日期、修改
                  // 者及修改内容简述  
    1. Date:
       Author:
       Modification:
    2. ...
*************************************************/

5.源文件头部应进行注释,列出:版权说明、版本号、生成日期、作者、模块目的/功能、主要函数及其功能、修改日志等。(对应2.3)

示例:下面这段源文件的头注释比较标准,当然,并不局限于此格式,但上述信息建议要包含在内。

/************************************************************
Copyright (C), 1988-1999, Huawei Tech. Co., Ltd.
FileName: test.cpp
Author: Version : Date:
Description: // 模块描述
Version: // 版本信息
Function List: // 主要函数及其功能
1. -------
  History:         // 历史修改记录

      David    96/10/12     1.0     build this moudle  
***********************************************************/

说明:Description一项描述本文件的内容、功能、内部各部分之间的关系及本文件与其它文件关系等。History是修改历史记录列表,每条修改记录应包括修改日期、修改者及修改内容简述。

6.函数头部应进行注释,列出:函数的目的/功能、输入参数、输出参数、返回值、调用关系(函数、表)等。(对应2.4)

示例:下面这段函数的注释比较标准,当然,并不局限于此格式,但上述信息建议要包含在内。

/*************************************************
  Function:       // 函数名称
  Description:    // 函数功能、性能等的描述
  Calls:          // 被本函数调用的函数清单
  Called By:      // 调用本函数的函数清单
  Table Accessed: // 被访问的表(此项仅对于牵扯到数据库操作的程序)
  Table Updated:  // 被修改的表(此项仅对于牵扯到数据库操作的程序)
  Input:          // 输入参数说明,包括每个参数的作
                  // 用、取值说明及参数间关系。
  Output:         // 对输出参数的说明。
  Return:         // 函数返回值的说明
  Others:         // 其它说明
*************************************************/

7.在程序块的结束行右方加注释标记,以表明某程序块的结束。(对应2.19) 说明:当代码段较长,特别是多重嵌套时,这样做可以使代码更清晰,更便于阅读。

示例:参见如下例子。

if (...)
{
    // program code
    while (index < MAX_INDEX)
    {
        // program code
    } /* end of while (index < MAX_INDEX) */ // 指明该条while语句结束
} /* end of  if (...)*/ // 指明是哪条if语句结束

-----|标识符命名|-----

8.除了编译开关/头文件等特殊应用,应避免使用EXAMPLETEST_之类以下划线开始和结尾的定义。(对应3.9)



-----|变量,结构|------

9.去掉没必要的公共变量。(对应5.1)

说明:公共变量是增大模块间耦合的原因之一,故应减少没必要的公共变量以降低模块间的耦合度。

10.不要设计面面俱到、非常灵活的数据结构。(对应5-4)

说明:面面俱到、灵活的数据结构反而容易引起误解和操作困难。

(个人理解,这个也是片面的,关键还是在于开发者把握好尺度的问题)

11.留心具体语言及编译器处理不同数据类型的原则及有关细节。(对应5.9)

说明:如在C语言中,static局部变量将在内存“数据区”中生成,而非static局部变量将在“堆栈”中生成。这些细节对程序质量的保证非常重要。

-----|函数,过程|-----

12.编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P、V操作)等手段对其加以保护。(对应6.4) 说明:若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。

13.为简单功能编写函数。(对用6.4) 说明:虽然为仅用一两行就可完成的功能去编函数好象没有必要,但用函数可使功能明确化,增加程序可读性,亦可方便维护、测试。

示例:如下语句的功能不很明显。

value = ( a >; b ) ? a : b ;

改为如下就很清晰了。

int max (int a, int b)
{
      return ((a > b) ? a : b);
}
value = max (a, b);

或改为如下。

#define MAX (a, b) (((a) > (b)) ? (a) : (b))
value = MAX (a, b);

(个人理解:#define这个不太推荐,放着好好的函数不用,何必徒增烦恼?)

-----|可测性|-----

14.使用断言来发现软件问题,提高代码可测性。(对应7.5) 说明:断言是对某种假设条件进行检查(可理解为若条件成立则无动作,否则应报告),它可以快速发现并定位软件问题,同时对系统错误进行自动报警。断言可以对在系统中隐藏很深,用其它手段极难发现的问题进行定位,从而缩短软件问题定位时间,提高系统的可测性。实际应用时,可根据具体情况灵活地设计断言。

15.用断言确认函数的参数。(对应7.9)

示例:假设某函数参数中有一个指针,那么使用指针前可对它检查,如下。

int exam_fun( unsigned char *str )
{
    EXAM_ASSERT( str != NULL ); // 用断言检查“假设指针不为空”这个条件
    ... //other program code

16.使用代码检查工具(如C语言用PC-Lint)对源程序检查。 使用软件工具(如 LogiSCOPE)进行代码审查。

(以上两个我也没用过,这几天研究下)


-----|宏|-----

17.将宏所定义的多条表达式放在大括号中。(对应13.2)

示例:下面的语句只有宏的第一条表达式被执行。为了说明问题,for语句的书写稍不符规范。

#define INTI_RECT_VALUE( a, b )\
a = 0;\
b = 0;
for (index = 0; index < RECT_TOTAL_NUM; index++)
INTI_RECT_VALUE( rect.a, rect.b );

正确的用法应为:

#define INTI_RECT_VALUE( a, b )\
{\
a = 0;\
b = 0;\
}

for (index = 0; index < RECT_TOTAL_NUM; index++)
{
    INTI_RECT_VALUE( rect[index].a, rect[index].b );
}

19:使用宏时,不允许参数发生变化。(对应12.3)

示例:如下用法可能导致错误。

#define SQUARE( a ) ((a) * (a))
int a = 5;
int b;
b = SQUARE( a++ ); // 结果:a = 7,即执行了两次增1。

正确的用法是:

b = SQUARE( a );
a++; // 结果:a = 6,即只执行了一次增1。