Checklist 项

1:缩进

程序块需要采用统一的缩进和对齐,整个项目中或者是4个空格或者是一个TAB,不允许混用。

1)缩进要求:那可能说什么是缩进,打个比方在使用 if else 语句进行判断时为了书写不容易出错,一般将这两个关键字对齐

if (a>1)
{
// ...执行语句
}
else
{
// ...执行语句
}

(A)if else case for while 语句需要缩进。

(B)case 语句与所属的switch语句对齐

(C)所有的{}需要缩进,extern "C", namespace 块除外,case语句除外

2)空格的使用

(A)关键字 if else switch case for while 之后要加空格

(B)如果 , ;后面没有立即换行,要在后面加上空格。

(C)小括号内侧不能有空格,函数调用或宏的名字与括号之间不能有空格

(D)一元操作符 & * + - ~ ! ++ --要紧贴对应的变量,不能有空格

(E)二元操作符 =  +  -  *  /  %  &  |  ^  ==  !=  >=  <=  >  <  ?: 两侧要加括号

(F)结构体成员操作符 .  ->前后不加空格

2:语句行

一行只写一条语句,不允许把多个短语句写到一行中

3:大括号

1)‘}’必须独占一行,但是有两种情况除外:

       A:如果是if(...){} else if(...) else {}中,可与else 放同一行

       B:如果是在do{}while(...)中,可与while放同一行

2) ‘{’可以独占一行,且与上一语句的起始位置放同一行

       也可以跟在相应的if ,for ,do ,while ,switch ,class ,声明或者函数声明后面

3) '{','}'的相对位置在整个模块中必须保持一致。
    if、for、do、while、switch这几种语句块的'{'必须保持相对位置一致。

    其他语句块的'{'只要求在同类型之间保持相对位置一致即可。

4:代码行长度

每行代码不应超过80列,但注释例外,如果字符串单独占一行扔超过80列,可以例外。

5:声明注释

1)文件头:在头文件(*.h,*.cpp,*.inc)和源文件头部应注释说明其功能

2)函数头:函数头部应该注释说明其功能以及各参数,返回值的含义(无参构造函数,析构函数,重载运算符函数可以不用)

3)全局变量:全局变量应注释说明其功能

4)常量:所有常量定义都应该注释说明其功能

5)类型:所有类型定义包括(struct,class,enum,union),都应该注释其功能

6)宏定义:所有的宏定义都应注释说明其功能如果宏有参数,必须说明参数的用法

6:语句注释

在下述控制结构处应按要求进行注释,语句块少于五行的允许例外。

1)if语句的各个分支,注释说明条件和具体功能

2)for/while/do的头部,注释说明判断条件好具体的功能

3)switch头部,注释说明判断条件和具体条件

--------------------------------------------------------------------------------
函数宏注释范例:
--------------------------------------------------------------------------------
/**
 *    释放内存,并把指针清零,防止重复释放
 *    @param ptr 内存块指针,只允许传入malloc/remalloc/strdup等
 *                C库函数分配的内存块指针
 */
#define FREE_ZERO(ptr)             \
    do {                           \
        if (!(ptr)) {              \
            free(ptr);             \
            ptr = NULL;            \
        }                          \
    } while(0)

--------------------------------------------------------------------------------
宏注释范例:
--------------------------------------------------------------------------------
#define MAX_BUF_SIZE 200    // 缓冲区的最大长度
// 缓冲区的最大长度
#define MAX_BUF_SIZE 200    
--------------------------------------------------------------------------------
文件头注释范例:
--------------------------------------------------------------------------------
/*
  功能:本文件定义文件列表的接口,文件列表是个存储文件路径名和文件信
           息的列表,本文件提供了文件列表的存储、读取、定位、访问等接口。
  日期:2011-3-4
  作者:zbc
*/
#ifndef FILELIST_H_
#define FILELIST_H_

#endif //FILELIST_H_

--------------------------------------------------------------------------------
函数头注释范例:
--------------------------------------------------------------------------------
/**
 * 发送数据给服务器
 * @param [in]buf  数据缓冲区指针
 * @param len   数据长度
 * @return <0表示失败,否则表示实际发送成功的字节数
 * @sample
 *  int len = send_buf(buf, buflen);
 *  if (len < 0) {
 *   DUMP("send failed, errno: %x\n", len);
 *   return -1;
 *  } else {
 *   DUMP("send ok, already send %d bytes.\n", len);
 *  }
 */
int send_buf(const char* buf, int len);
--------------------------------------------------------------------------------
全局变量注释范例:
--------------------------------------------------------------------------------
char g_log_fname[MAX_PATH]; // 日志文件的路径名称,从配置文件中读取得到
// 日志文件的路径名称,从配置文件中读取得到
char g_log_fname[MAX_PATH]; 
--------------------------------------------------------------------------------
类型注释:
--------------------------------------------------------------------------------
/**
 *  文件列表类
 *    @remark
 *        可用于记录系统所缓存的所有小文件,该列表可存在磁盘上
 *    @note
 *        该类不具备线程安全性,多线程同时访问需加锁
 */
class filelist {
...
private:
    std::vector<std::string> m_fnames;        // 列表中保存的所有文件的名字
    std::string m_name;                       // 列表的名字
};

--------------------------------------------------------------------------------
if 语句注释:
--------------------------------------------------------------------------------
// 将buf中保存的消息发送给CGI进程
ret = send(sk, buf, datalen, 0);
if (ret < 0) {                // 没有发送成功
    LOGDBG("send failed, errno: %d\n", errno);
} else if (ret == 0) {        // 对端关闭
    LOGDBG("send failed, peer shutdown\n");
} else {                      // 发送成功
    LOGDBG("send ok, length: %d\n", ret);
}

--------------------------------------------------------------------------------
for 语句注释:
--------------------------------------------------------------------------------
// 遍历文件列表,直到找到名字为filename的文件对象,或列表遍历完毕
for (int i = 0 ; i < cnt ; ++i) {
    if (strcmp(filename, filelists[i]->name) == 0)
        return i;
}
--------------------------------------------------------------------------------
while/do...while 语句注释:
--------------------------------------------------------------------------------
// 将所有用户配置文件打包
pdir = opendir("/var/sangfor");
if (!pdir) {
    ...
    return -1;
}
// 遍历/var/sangfor目录下所有配置文件(*.conf),通过tar命令将其打到压缩包
while ((pcur = readdir(pdir)) != NULL) {
    ...

}

7  废弃代码

确定不使用的功能代码要删除,或者通过注释或者#if 0 关闭

8  命名风格

在同一模块中要有自己统一的风格,或者全小写加下划线,或者大小写混排,或者Java风格,但是不要混用。

9  命名要求

1)不使用拼音

2)不使用无意义的字母组合

3)除循环变量可使用i,j,k,指针变量可使用p之外,不使用单字符的名字

4)不使用下划线开头

5)非静态全局变量使用g_ 开头

6)静态全局变量使用s_开头

7)类成员变量使用m_开头,等同于c结构体的可以例外,union可以例外

8)局部变量不加前缀

10 文件名

使用include包含的文件名全部使用小写。Windows界面相关的代码文件运行例外

11 魔数

不允许使用0,1,-1之外的魔数,有需要用到数字的地方,请用命名常量来代替。如果作为标识(比方说状态标识)时,0,1,-1也不允许 直接使用。所谓魔数,指以字面值形式出现的数值常量(不包括字符串常量),比如3,-4,256,3.14,0.628。

但有的情况是允许的:

(A)初始化一个变量是允许使用魔数 如int num=20;timeval tv={10,10};

(B)定义代替魔数的命名常量是允许使用魔数如 #define PI 3.14;

(C)该魔数代表参数个数,且参数个数无法通过sizeof等方法测量得到的

(D)0,-1作为返回值,分别代表正常和出错是允许的。同理1,0代替布尔值进行运算也是可以的

(E)作为位运算或其他算法的固有参数时允许使用魔数,但是需要注释说明

(F)该魔数只跟当前语句有关(即无需与模块其它代码保持一致),且替换为标识符常量后对代码可读性没有明显提升,

    则允许直接使用魔数

定义标识符常量替代魔数的原则是该标识符必须包含更丰富的信息,以提升代码可读性。
典型的可以指明上下文,指明常量的含义(如:USRCFG_USERNAME_MAXSIZE,指在用户配置中用到的用户名的最大长度)
以下命名方式是常见的误区:
(A) 直接在标识符常量中出现魔数本身,如:#define BUFSIZE_128 128
(B) 使用含糊的语义,如:#define LEN 128

(C) 把字符串常量也当成魔数,如:#define MKDIR_ERROR_STR "mkdir error, errno(%d):%s\n"

不可以这样:
1) return 2;
2) double area = 3.14*radius*radius;
3) mkdir(usrcfg_fname, 0660);
可以这样:
1)   在头文件中有常量的统一定义:
      const int EOUTMEM = 2;  或#define EOUTMEM 2
   使用时引用已经定义好的常量:
      return EOUTMEM;
2) #define PI 3.14
    double area = PI*radius*radius;
3) #define USRCFG_UGO_RW_RW_NONE 0660
    mkdir(usrcfg_name, USRCFG_UGO_RW_RW_NONE);

作为特例,允许使用魔数的情形:1)魔数代表参数个数:
int ret = sscanf(sz, "%u.%u.%u.%u", &ip1, &ip2, &ip3, &ip4);
if (ret != 4) {
    printf("read ip failed\n");
}

该代码中,4代表读取到的参数个数。

12: 变量名称和用途匹配

1)变量名字与实际用途相符

2)在变量的作用域当中,一个变量不用作多个用途

3)如果变量有个多个取值范围且各取值范围代表不用的含义,须保证各个取值范围之间不得有重叠

实例:

1) 完全无关的命名:
比如:count用于表示颜色;

2) 名字和用途相反:
比如:free_cnt用于表示当前存活的对象数目;

3) 一个变量多个用途:
一个实际引起了BUG的例子:value既作为入参表示哈希值,又作为出参,表示是否成功。
#define HASH_INSERT(hash_table, node, value, type)  \
    do {                                            \
        node->pre = NULL;                           \
        node->pnext = NULL;                         \
        unsigned int h_i_i = value % HASH_SIZE;     \
        if (hash_table[h_i_i] != NULL &&            \
            hash_table[h_i_i]->key != node->key) {  \
            ...                                     \
        }else if (hash_table[h_i_i] == NULL) {      \
            hash_table[h_i_i] = node;               \
        } else {                                    \
            value = 0;                              \
        }                                           \
    } while (0);

4) 同一个用途,多次使用是允许的,如:
int ret = 0;
ret = init_pools();
ret = init_threads();
ret = init_plugins();

5) 不同取值范围代表不同意思,比如:
int find_string(const vector<string>& lst, const char* name);
find_string的功能是从字符串列表中查找是否存在字符串name,当返回值大于0时,表示name在字符串列表lst中的索引,==0表示未找到,<0时查找过程出错,并指示出错类型。

如果0也是列表的合法索引值,则find_string违反了本条款的第3项,即:取值范围有重叠,0既可能是索引值,也可能代表没找到。

13:减小标识符的作用域和可见性

1)不允许在头文件中定义非静态全局变量仅仅声明除外。

2)不被别的编译单元访问的全局变量,必须声明为静态全局变量即加static修饰

3)不被别的编译单元访问的函数必须加上static声明

4)关于类之间的private与protect的引用,

如果要违反上诉2,3,4项 注释说明请原因可以例外。

例如:全局变量定义:
int g_debug;
int g_debug = 1;
全局变量声明:
extern int g_debug;
静态全局变量:

static int s_debug;

14:函数声明

1)函数的声明必须和函数定义的保持原型一致。

2)引用其他模块的或.c/.cpp文件提供的外部链接特性的函数,应使用include头文件的方式引用函数声明,不允许自行声明

3)提供给 .c文件使用的函数声明必须放在extern "C" {}域内,并通过宏防止问题

如提供给c模块使用的函数原型声明:

#ifdef __cplusplus

extern "C" {
#endif

void foo(int xxx, int yyy);

#ifdef __cplusplus
}
#endif

15:函数参数

1)函数调用传递大对象(超过八个字节大小)时不使用按值传递,返回值允许例外。

2)不允许在参数中使用布尔类型但是函数可申明为布尔类型。

3)函数参数中不得定义数组的参数,应使用指针代替数组。如果数组定义没有指定数组的长度也可以

4)函数参数个数<=5

5)最好拆分函数的功能,就可以分多个函数书写,功能和条理也会更清晰。

注1:
以下函数声明违反checklist:
1) void func(string name);
2) struct record {
    int type;
    int len;
    char data[16];
};
void send_record(record rec);
注2:
函数参数中使用布尔类型,会使代码更难以理解,如:
UpdateData(TRUE)
CreateProcess(chPath, "", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)
如果不是对这些函数非常熟悉,你知道这些TRUE,FALSE代表什么意思吗?

如果改为UpdateData(SAVE_VALIDATE),CreateProcess(chPath, "", NULL, NULL, NO_INHERIT_HANDLE, 0, NULL, NULL, &si, &pi)会不会好一点?
改造方法:
UpdateData是MFC函数,原型如下:

BOOL UpdateData(BOOL bSaveAndValidate);

下述修改方法都可以提高UpdateData的可读性:

1)分拆成两个函数实现
BOOL UpdateData();
BOOL UpdateData_SaveAndValidate();

2)将bSaveAndValidate参数改造成枚举
enum SaveAndValidate_e{
    SAVE_VALIDATE,
    NO_SAVE_VALIDATE
};
BOOL UpdateData(enum SaveAndValidate_e eSave);
这样,在调用这个函数时就有一个名字了,可以通过枚举的名字更好的理解这个函数调用的意思,如:UpdateData(SAVE_VALIDATE);

注3:
void test_arr_copy(int arr[ARRAY_SIZE])
{
    int buf[ARRAY_SIZE];
    memcpy(buf, arr, sizeof(arr));
}
以上代码违反子条款3,sizeof(arr)的结果为sizeof(int),而非sizeof(int)*ARRAY_SIZE
可改为:
void test_arr_copy(int *arr)
{
...

}

以下函数定义不违反本条款(因为p是指向数组的指针,而非数组):

void test_arr_copy(int (*p)[ARRAY_SIZE]);

16:返回值

1)不返回本函数内定义的非静态局部变量的地址(包括易指针或引用形式返回),同样类成员变量也是不允许返回的

2)在返回值中使用的函数指针必须使用typeof定义的别名。

typedef int (*PfnScanner)();

PfnScanner get_scanner(const char *name);

17:const的使用

对于指针或引用参数,如果函数内部不改变该参数的值且不调用需要该参数为左值的函数,则必须在该参数定义前加const修饰

18:重复代码提炼成函数:
超过5行(不算空白行、注释行、只有大括号的行)的重复代码应提炼成函数(特殊情况也可提炼为宏)。

如有效率要求,可将函数内联。重复代码,指满足以下所有条件的代码块:

(A) 出现次数超过2次;

(B) 去除分隔符(比如空白符、大括号、注释等)以后,只有变量名字或常量内容不同;

19:格式化字符串的变参函数定义:
如果需要定义类似于printf的拥有可变参数列表、能格式化输出字符串的函数,需要在函数声明中加上检查参数有效性的属性声明,如下:

void my_print(int fd, const char *fmt, ...) __attribute__((format(printf, 2, 3)))

那么format指令的参数该怎么定?
Format指令有三个参数:format(printf,fmt_pos,args_pos),此处固定第一个参数为printf,第二个参数为格式串参数在函数参数列表中的位置(位置计数从1开始),第三个参数为变参开始的位置。
如:
void my_print(int fd, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
表示声明my_print为类printf函数,my_print的第二个参数为格式串,变参列表从第三个参数开始。

对于成员函数:
如果类printf函数是一个类的成员函数,format指令中的位置参数必须加1(即把this作为该函数的第一个参数),如:
class strings {
    void print(int fd, const char *fmt, ...) __attribute__((format(printf,3,4)));

};

20:命名:

除非有特殊理由,否则宏使用全大写加下划线的方式命名

21:括号使用(优先级):
1) 用宏定义表达式时,要使用完备的括号。
   保证该宏的所有可能的使用方法(即包含潜在的某些使用方法)都不会发生运算符优先级问题;

2) 如果宏定义中包含多条语句或者包含有if语句,须将这些语句放在大括号中,建议使用do{…}while(0)的形式;

例子:
正确:#define MAX_NUM(a,b) ((a) > (b) ? (a) : (b))
错误:#define MAX_NUM(a,b) (a > b ? a : b)
错误:#define MAX_NUM(a,b) (a) > (b) ? (a) : (b)
在这样用时上述两个错误例子都会发生错误:
size = MAX_NUM(maxsize&0xffffff00,cursize&0xffffff00)*20

如果宏含有多条语句,或含有if语句:

#define ASSERT(expr) do { if (!(cond)) { abort();}} while (0)

有些人认为必须将每个参数都用括号包起来,否则就是违反checklist,其实这种看法是不正确的。checklist只是规定必须“保证该宏的所有可能的使用方法(即包含潜在的某些使用方法)都不会发生运算符优先级问题”。即:
如果把宏当成一个函数使用,任何一种用法能正常编译通过,但结果不符合预期的话,那这个宏就是有问题的。
如果任何一种可能的用法都不会导致优先级问题(或者直接就会编译出错),那这个宏定义就是没有问题的,里面是否将所有参数都用括号包起来并没有什么关系。

如:
1) #define MAX(a,b,c) max(max(a,b), max(b,c))
宏参数中不会出现比逗号优先级低的运算符,所以无论怎么用都不会发生优先级问题。

2) #define GET_FUNC(state) g_funcs[state]
[]已经有括号的作用了,所以无需再用括号包起来。

2)#define mylog(format, ...) fprintf(stderr, "[%s,%s,%d] " format, __FILE__, __func__, __LINE__,  ## __VA_ARGS__)

在这个宏中,format参数不能传一个表达式,只能传字符串,否则会导致编译出错,所以也不会导致优先级问题。

22:宏参数:
使用宏时,不允许参数中出现会发生副作用(即会改变某些变量的值或程序的运行环境)的表达式。
比如不允许使用MAX(++a, b+=2)。比如:
#define MAX(x, y) ((x) > (y) ? (x) : (y))
MAX(++a, b+=2)展开后就是:
((++a) > (b+=2) ? (++a) : (b+=2))

这个表达式结果和利用函数调用实现MAX的运算结果是不一样的。

23:防名字冲突:
宏内部如果需要定义局部变量,必须防止该变量和上下文中的变量名字冲突,建议加上特殊前缀或后缀。

Gcc编译器开启-Wshadow后对该问题会产生告警,需消除。

例如:
//遍历列表
#define foreach_list(list, func)                    \
    do {                                            \
        int i;                                      \
        int cnt = list_size(list);                  \
        for (i = 0 ; i < cnt ; ++i) {               \
            func(list, i);                          \
        }                                           \
    } while (0)
    
假如调用者用法如下,就可能发生错误:
for (i = 0 ; i < cnt ; ++i) {
    foreach_list(&lists[i], do_print_list);
}
经过宏替换后,代码如下:
for (i = 0 ; i < cnt ; ++i) {
    do {
        int i;
        int cnt = list_size(&lists[i]);
        for (i = 0 ; i < cnt ; ++i) {
            do_print_list(&lists[i], i);
        }
    } while (0);
}
由于内层作用域定义的名字自动屏蔽外层定义的名字,编译器不会报错。

这样一来,原本foreach_list(&lists[i],do_print_list)是想访问外层的i,但经宏替换后实际访问到的i却是内层定义的。

解决办法是--给宏内部定义的局部变量加特殊前缀或后缀, 如:将int i;int cnt;改为int i_;int cnt_;

24:括号使用:
以下情况必须使用括号明确表达式优先级:
1) 同时出现 &、^、| 这三种运算符中的任意两种(或&、^同时出现两次);
2) 同时出现位运算符(& ^ |)和比较运算符(< <= > >= == !=)
3) 同时出现&&和||;
4) 同时出现移位运算符(<< >>)和比较运算符(< <= > >= == !=)
5) 同时出现比较运算符中(< <= > >= == !=)的任意两种(或一种出现两次);
6) 同时出现位运算符(& ^ |)和逻辑运算符(&& ||);
7) 同时出现移位运算符(<< >>)和算术运算符(+ - * / %)

如果一个上述运算符没有同时出现在一个操作数(或高优先级表达式)的两边,可以不做要求。

不符合要求:
1) if (op_bits & OP_READ && op_bits & OP_WRITE)
&和&&同时出现在OP_READ的两边
2) if (1 > mid != max)
>和!=同时出现在mid的两边
3) ret = op_bits | OP_READ & OP_MASK;
|和&同时出现在操作数OP_READ的两边;
4) ret = op_bits | (g_def_opbits & 0xffff) & OP_MASK;
|和&同时出现在高优先级表达式(g_def_opbits&oxffff)的两边;
5) if (pb == 0 || len == 0 && pe == 0)
||和&&同时出现在高优先级表达式len == 0的两边;
符合要求:
1) if ((op_bits & OP_READ) && (op_bits & OP_WRITE)) 
以上表达式已使用括号明确了优先级
2) if (mid >= min && mid < max)
以上表达式中>= 和 <没有同时出现在某个高优先级表达式的两边,中间的&&运算符优先级比较低。

3) if (p && p < pEnd && p > pBegin + 1)

25:goto使用限制:
只允许在同一个块作用域内跳转,或者跳转到上层的块作用域。
不得用于跳转到更深的块作用域或者其它平行的块作用域。

不允许使用goto在switch的多个case语句/default语句之间跳转;

例如:下述代码是不符合要求的。

if (ok)
   goto ready;
func();
while(1){
ready:
//…

}

26:循环性能优化:
可以在循环体外进行的耗时计算不放入循环体中。反例:
for(int i = 0; i < lst.count(); ++i) {
    printf("%d", lst[i]);
}                                                                 
注:lst是虚拟的一个list类,具有链表的含义,有count(利用遍历求链表长度)和operator[](遍历取指定位置成员)两个成员函数。此例会导致count不必要的重复计算,时间复杂度O(n*n).
类似的还有下面这段代码:
for(int i = 0; i < strlen(str); ++i) {
    if (str[i] == ' ') {
        break;
    }

}

27:不使用复杂表达式:
不使用过于复杂的表达式,如确实有必要这样写须注释说明该表达式的意思。鼓励把复杂表达式分拆成多句书写;
1) 所谓过于复杂的表达式,指一个运算数某一边的运算符个数大等于2个。
   单目运算符+,-,*,!,~,&,sizeof及括号[],()不计算在内。 
   如:*stat_poi ++ += 1;应拆分成*stat_poi += 1;++stat_poi;

2) ?:运算符不允许嵌套使用,如:ret = a < b ? (a < c ? a : c) : (b < c ? b : c);

28:switch/case语句:
1) 每个case语句必须以break语句(或continue/goto/return/longjmp/exit等流程转移语句)结束。
   如果不需要break,必须在末尾注释说明。
   如果该case标签后没有任何处理语句可以例外。没有处理语句的多个case标签可以写在一行;

2) 每个switch语句都必须要有default标签;

29:控制结构(if/for/while/switch等)的嵌套:
1) 不使用过深的嵌套:循环嵌套不超过3层,总共不超过5层;
2) 如果if子句和else子句行数相差超过3行,须保证else子句比if子句长。
   如果该条件语句有多个条件,可以例外(即存在else if子句)。

建议使用短路返回的方法减少嵌套层数;

不合格嵌套:
for(int row = 0; row < rowcnt; ++row) {
    for(int col = 0; col < colcnt; ++col) {
        char *pname = g_pool[row][col].name;
        for(int i = 0; i < MAX_NAME_LEN; ++i) {
            char ch = pname[i];
            for(int j = 0; j < PREDEF_TABU_SIZE; ++j) {
                if (ch == g_predef_tabu[j]) {
                    return false;
                }
            }
            ...
        }  
    }
}
可以改造为:
inline bool is_tabu(char c)
{
    for(int j = 0; j < PREDEF_TABU_SIZE; ++j) {
        if (ch == g_predef_tabu[j]) {
            return true;
        }
    }
    return false;

}

for(int row = 0; row < rowcnt; ++row) {
    for(int col = 0; col < colcnt; ++col) {
        char *pname = g_pool[row][col].name;
        for(int i = 0; i < MAX_NAME_LEN; ++i) {
            char ch = pname[i];
            if (is_tabu(ch))
                return false;
            ...
        }  
    }
}
[短路返回]
if (match_condition()) {
    ... ...
    if (...)
        ... ...
} else {
    break;
}
改为以下语句就是短路返回:
if (!match_condition())
    break;
... ...
if (...)

    ... ...

30: 自增/自减运算:
表达式计算结果不能依赖于副作用计算发生的时机。
(A) 同一语句中不得对同一变量使用多次自增或自减运算符。比如:*p++ = 2 + *p++;

(B) 不允许在一个表达式中既对该变量赋值,又对该变量使用自增/自减运算符。

31:参数顺序依赖性:
函数调用参数列表中,参数值的计算不得有顺序依赖性。

比如:Call(a = b, ++a);Call(foo1(), foo2());其中foo1和foo2的执行顺序不同会造成不同结果。

32: 除0错误预防(包括求余运算):例如:
size_t unitsize = ini_get("unitsize");
if (unitsize == 0)
    unitsize = 1;
size_t unitnum = size / unitsize;
用作除数的变量需保证不为0。
求余运算符的右操作数也需保证不为0。

如果该变量来自不可信的输入(外部输入或者其它模块传递的参数),必须先判断是否为0,为0时不作为除数参与运算。

33:指针转换:

void*类型的指针和其它类型的指针之间必须使用强制转换;

34:移位运算:
移位运算的右操作数(即移动位数)必须大等于0并小于左操作数的位数;

猜你喜欢

转载自blog.csdn.net/abandoninged/article/details/80280380