编程笔记

lifelong learning & practice makes perfect

Coding Standard

阅读更多 < !–more– >

1、排版

  1. 一般情况下,源程序有效注释量必须在20%以上。

说明:注释的原则是有助于对程序的阅读理解,在该加的地方都加了,注释不宜太多也不能太少,注释语言必须准确、易懂、简洁。

  1. 相对独立的程序块之间、变量说明之后必须加空行。

示例:如下例子不符合规范。

if (!valid_ni(ni))

{

\... // program code

}

repssn_ind = ssn_data[index].repssn_index;

repssn_ni = ssn_data[index].ni;

应如下书写

if (!valid_ni(ni))

{

\... // program code

}

repssn_ind = ssn_data[index].repssn_index;

repssn_ni = ssn_data[index].ni;

  1. 较长的语句要分成多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进行适当的缩进,使排版整齐,语句可读。

示例:

perm_count_msg.head.len = NO7_TO_STAT_PERM_COUNT_LEN

+ STAT_SIZE_PER_FRAM

* sizeof( _UL );

act_task_table[frame_id * STAT_TASK_CHECK_NUMBER + index].occupied

= stat\_poi\[index\].occupied;

act_task_table[taskno].duration_true_or_false

= SYS\_get\_sccp\_statistic\_state( stat\_item );

report_or_not_flag = ((taskno < MAX_ACT_TASK_NUMBER)

&& (n7stat\_stat\_item\_valid (stat\_item))

&& (act\_task\_table\[taskno\].result\_data != 0));
  1. 循环、判断等语句中若有较长的表达式或语句,则要进行适应的划分,长表达式要在低优先级操作符处划分新行,操作符放在新行之首。

示例:

if ((taskno < max_act_task_number)

&& (n7stat\_stat\_item\_valid (stat\_item)))

{

\... // program code

}

for (i = 0, j = 0; (i < BufferKeyword[word_index].word_length)

&& (j \< NewKeyword.word\_length); i++, j++)

{

\... // program code

}

for (i = 0, j = 0;

(i \< first\_word\_length) && (j \< second\_word\_length);

i++, j++)

{

\... // program code

}

  1. 若函数或过程中的参数较长,则要进行适当的划分。

示例:

n7stat_str_compare((BYTE *) & stat_object,

(BYTE \*) & (act\_task\_table\[taskno\].stat\_object),

sizeof (\_STAT\_OBJECT));

n7stat_flash_act_duration( stat_item, frame_id *STAT_TASK_CHECK_NUMBER

+ index, stat\_object );
  1. 不允许把多个短语句写在一行中,即一行只写一条语句。

示例:如下例子不符合规范。

rect.length = 0; rect.width = 0;

应如下书写

rect.length = 0;

rect.width = 0;

  1. if、for、do、while、case、switch、default等语句自占一行,且if、for、do、while等语句的执行语句部分无论多少都要加括号{}。

示例:如下例子不符合规范。

if (pUserCR == NULL) return;

应如下书写:

if (pUserCR == NULL)

{

return;

}

  1. 程序块的分界符(如C/C++语言的大括号’{‘和’}’)应各独占一行并且位于同一列,同时与引用它们的语句左对齐。在函数体的开始、类的定义、结构的定义、枚举的定义以及if、for、do、while、switch、case语句中的程序都要采用如上的缩进方式。

示例:如下例子不符合规范。

for (...) {

\... // program code

}

if (...)

{

\... // program code

}

void example_fun( void )

{

\... // program code

}

应如下书写。

for (...)

{

\... // program code

}

if (...)

{

\... // program code

}

void example_fun( void )

{

\... // program code

}

  1. 一行程序以小于80字符为宜,不要写得过长。

2、注释

  1. 一般情况下,源程序有效注释量必须在20%以上。

说明:注释的原则是有助于对程序的阅读理解,在该加的地方都加了,注释不宜太多也不能太少,注释语言必须准确、易懂、简洁

  1. 注释格式尽量统一,建议单行注释采用”//…”,多行注释采用”/* ……

    //*/“。注释采用中文

  2. 源文件头部(如头文件.h文件、.inc文件、.def文件、编译说明文件.cfg等)应进行注释,列出:版权说明、版本号、生成日期、作者、模块目的/功能、主要函数及其功能、修改日志等,头文件的注释中还应有函数功能简要说明。

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

/*************************************************

Copyright (C), 1988-1999, Huawei Tech. Co., Ltd.

File name: // 文件名

Author: Version: Date: // 作者、版本及完成日期

Description: // 用于详细说明此程序文件完成的主要功能,与其他模块

// 或函数的接口,输出值、取值范围、含义及参数间的控

// 制、顺序、独立或依赖等关系

Others: // 其它内容的说明

Function List: // 主要函数列表,每条记录应包括函数名及功能简要说明

1. \....

History: // 修改历史记录列表,每条修改记录应包括修改日期、修改

               // 者及修改内容简述

1. Date:

   Author:

   Modification:

2. \...

*************************************************/

说明1:Description一项描述本文件的内容、功能、内部各部分之间的关系及本文件与其它文件关系等。

History是修改历史记录列表,每条修改记录应包括修改日期、修改者及修改内容简述。

示例2: .h中声明函数原型

//--获得当前应用程序的全名

CString GetAppExeFileFullName();

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

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

/*************************************************

Function: // 函数名称

Description: // 函数功能、性能等的描述

Calls: // 被本函数调用的函数清单

Called By: // 调用本函数的函数清单

Table Accessed: // 被访问的表(此项仅对于牵扯到数据库操作的程序)

Table Updated: // 被修改的表(此项仅对于牵扯到数据库操作的程序)

Input: // 输入参数说明,包括每个参数的作

// 用、取值说明及参数间关系。

Output: // 对输出参数的说明。

Return: // 函数返回值的说明

Others: // 其它说明

*************************************************/

示例2:

//***********************************************************

//函数类别: 系统操作

//函数名称: GetAppExeFileFullName

//函数用途: 获得当前应用程序的全名

//原始作者: 陆 宏 伟

//原始日期: 12/4 /1999

//***********************************************************

CString GetAppExeFileFullName()

{

char ModuleFileName[_MAX_PATH];

GetModuleFileName( NULL, ModuleFileName, _MAX_PATH);

return CString( ModuleFileName );

}

  1. 边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。

  2. 注释应与其描述的代码相近,对代码的注释应放在其上方或右方(对单条语句的注释)相邻位置,不可放在下面,如放于上方则需与其上面的代码用空行隔开。

示例:如下例子不符合规范。

repssn_ind = ssn_data[index].repssn_index;

repssn_ni = ssn_data[index].ni;

/* get replicate sub system index and net indicator */

应如下书写,将注释放于上方

/* get replicate sub system index and net indicator */

repssn_ind = ssn_data[index].repssn_index;

repssn_ni = ssn_data[index].ni;

  1. 对于所有的变量、常量,如果其命名不是充分自注释的,在声明时都必须加以注释,说明其含义。变量、常量、宏的注释应放在其上方相邻位置或右方。

示例:

/* active statistic task number */

#define MAX_ACT_TASK_NUMBER 1000

#define MAX_ACT_TASK_NUMBER 1000 /* active statistic task number */

  1. 数据结构声明(包括数组、结构、类、枚举等),如果其命名不是充分自注释的,必须加以注释。对数据结构的注释应放在其上方相邻位置,不可放在下面;对结构中的每个域的注释放在此域的右方。

示例:可按如下形式说明枚举/数据/联合结构。

/* sccp interface with sccp user primitive message name */

enum SCCP_USER_PRIMITIVE

{

N\_UNITDATA\_IND, /\* sccp notify sccp user unit data come \*/

N\_NOTICE\_IND,    /\* sccp notify user the No.7 network can not \*/

                 /\* transmission this message \*/

N\_UNITDATA\_REQ, /\* sccp user\'s unit data transmission request\*/

}

  1. 全局变量要有较详细的注释,包括对其功能、取值范围、哪些函数或过程存取它以及存取时注意事项等的说明。

示例:

/* The ErrorCode when SCCP translate */

/* Global Title failure, as follows */ // 变量作用、含义

/* 0 - SUCCESS 1 - GT Table error */

/* 2 - GT error Others - no use */ // 变量取值范围

/* only function SCCPTranslate() in */

/* this modual can modify it, and other */

/* module can visit it through call */

/* the function GetGTTransErrorCode() */ // 使用方法

BYTE g_GTTranErrorCode;

  1. 注释与所描述内容进行同样的缩排。

说明:可使程序排版整齐,并方便注释的阅读与理解。

示例:如下例子,排版不整齐,阅读稍感不方便。

void example_fun( void )

{

/* code one comments */

CodeBlock One



    /\* code two comments \*/

CodeBlock Two

}

应改为如下布局。

void example_fun( void )

{

/\* code one comments \*/

CodeBlock One



/\* code two comments \*/

CodeBlock Two

}

  1. 在代码的功能、意图层次上进行注释,提供有用、额外的信息。

说明:注释的目的是解释代码的目的、功能和采用的方法,提供代码以外的信息,帮助读者理解代码,防止没必要的重复注释信息。

示例:如下注释意义不大。

/* if receive_flag is TRUE */

if (receive_flag)

而如下的注释则给出了额外有用的信息。

/* if mtp receive a message from links */

if (receive_flag)

  1. 在程序块的结束行右方加注释标记,以表明某程序块的结束。

说明:当代码段较长,特别是多重嵌套时,这样做可以使代码更清晰,更便于阅读。

示例:参见如下例子。

if (...)

{

// program code



while (index \< MAX\_INDEX)

{

    // program code

} /* end of while (index < MAX_INDEX) */ // 指明该条while语句结束

} /* end of if (...)*/ // 指明是哪条if语句结束

3 、标识符命名

  1. 所有标识符的命名均由有意义的英文单词组成。较长的英文单词可以缩写,但是缩写的单词要比较容易辨别它的意义。

说明:较短的单词可通过去掉”元音”形成缩写;较长的单词可取单词的头几个字母形成缩写;一些单词有大家公认的缩写。

示例:如下单词的缩写能够被大家基本认可。

temp 可缩写为 tmp ;

flag 可缩写为 flg ;

statistic 可缩写为 stat ;

increment 可缩写为 inc ;

message 可缩写为 msg ;

规定的常用缩写如下:


常用词 缩写
argument arg
buffer buf
clear clr
clock clk
compare cmp
configuration配置 cfg
context ctx
delay dly
device dev
disable dis
display disp
enable en
error err
function fnct
hexadecimal十六进制 hex
decimal十进制 dec
initial init
maximum max
minimum min
multiplex mux
parameter param
previous以前(的) pre
pointer ptr
priority优先(级) prio
read rd
ready rdy
semaphore信号量 sem
synchronize同步 sync
timer tmr
trigger trig
write wr


  1. 命名中若使用特殊约定或缩写,则要有注释说明。

说明:应该在源文件的开始之处,对文件中所使用的缩写或约定,特别是特殊的缩写,进行必要的注释说明。

  1. 除非必要,不要用数字或较奇怪的字符来定义标识符。

示例:如下命名,使人产生疑惑。

#define _EXAMPLE_0_TEST_

#define _EXAMPLE_1_TEST_

void set_sls00( BYTE sls );

应改为有意义的单词命名

#define _EXAMPLE_UNIT_TEST_

#define _EXAMPLE_ASSERT_TEST_

void set_udt_msg_sls( BYTE sls );

  1. 用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。

说明:下面是一些在软件中常用的反义词组。

add / remove begin / end create / destroy

insert / delete first / last get / release

increment / decrement put / get

add / delete lock / unlock open / close

min / max old / new start / stop

next / previous source / target show / hide

send / receive source / destination

cut / paste up / down

示例:

int min_sum;

int max_sum;

int add_user( BYTE *user_name );

int delete_user( BYTE *user_name );

  1. 防止局部变量与公共变量同名。对于变量命名,禁止取单个字符(如i、j、k...),建议除了要有具体含义外,还能表明其变量类型、数据类型等,但i、j、k作局部循环变量是允许的

说明:变量,尤其是局部变量,如果用单个字符表示,很容易敲错(如i写成j),而编译时又检查不出来,有可能为了这个小小的错误而花费大量的查错时间。

示例:

公认的变量作用域前缀

m_ :类成员变量

g_ :全局变量

s_ :模块内静态变量

c_ :常量

空 :局部变量

这样可以防止局部变量与全局变量重名。

  1. 使用一致的小写类型指示符作为前缀来区分变量的类型。

说明:常用变量类型前缀列表如下:

i : int m_iSum;

f : float

d : double

c : char

uc : unsigned char 或 BYTE

l : long

p : pointer( 即 *)

b : BOOL

h : HANDLE

w : unsigned short 或 WORD

dw : DWORD 或 unsigned long

a : 数组:array of TYPE

str : 字符串

t : 结构类型

  1. 结构体、联合体、枚举名的定义以T_开头,声明其一个变量的前缀统一以 t_ 开头。结构体、联合体、枚举必须被类型化。

说明:这样子让别人一看这个变量就是结构体,大概知道里面包含多个变量,易于和其它变量区分。

例如:OpenCv里面的一个结构体IplROI声明如下:

//这样子定义一个结构体在后期使用时不太直观,无法感知IplROI就是一个结构体

typedef struct _IplROI

{

int coi; /* 0 - no COI (all channels are selected), 1 - 0th channel is selected ...*/

int xOffset;

int yOffset;

int width;

int height;

}

IplROI;

我们约定这样子声明:

正例:

typedef struct

{

int coi; /* 0 - no COI (all channels are selected), 1 - 0th channel is selected ...*/

int xOffset;

int yOffset;

int width;

int height;

}

T_IplROI;

//结构体、联合体、枚举必须被类型化

T_IplROI m_tIplROI; // m_t为前缀,m_为成员变量前缀,t为结构体标志前缀

  1. 资源命名

格式如下:IDR(IDS、IDB、IDD、IDC)_rrr(资源名称)_mmm(所属模块名称)

具体规则如下:

字符串:以”IDS_“开头,例如:IDS_VIEW

图片: 以”IDB_“开头,例如:IDB_COREICON

对话框:以”IDD_“开头,例如:IDD_CORE

对话框中的控件:以”IDC_控件简称“开头,

Menu Control : IDC_MENU_ 例如:IDC_MENU_SECURITY

Static Control : IDC_STATIC_ 例如:IDC_STATIC_MESSAGE

Icon View Control : IDC_ICONV 例如:IDC_ICONV_CORE

List Control : IDC_LIST_ 例如:IDC_LIST_SETTINGS

Text Control : IDC_TEXT_ 例如:IDC_TEXT_PIN

Image Control:IDC_IMAGE_ 例如:IDC_IMAGE_SECURITY

Date Control : IDC_DATE_ 例如:IDC_DATE_SETTINGS

Clock Control :IDC_CLOCK_ 例如:IDC_CLOCK_VOLUME

  1. 控件命名

1) 在类中定义的控件变量

控件名 = ‘m_‘ + 控件缩写 + 代表控件含义的英文名

例如:

CButton m_btnSubmit; // m_为成员变量前缀,btn为CButton控件缩写

2) 在普通函数中定义的控件变量

控件名 = 控件缩写 +代表控件含义的英文名

CButton btnSubmit;

  1. 完整的变量名命名规则为: 前缀+变量名主体,且变量名主体首字母必须大写。

说明:各种前缀字符可以组合使用,但顺序应为:变量作用域前缀、变量类型前缀。

例如:float g_fValue; // 类型为浮点型的全局变量

char *pcOldChar; //类型为字符指针的局部变量,*p为指针前缀,c为字符前缀

  1. 宏、常亮命都要使用大写字母,用下划线’_‘分割单词。预编译开关的定义使用下划线’_‘开始。

示例:如DISP_BUF_SIZE、MIN_VALUE、MAX_VALUE等等。

  1. 常量名必须用大写字母书写,单词之间用下划线分割。尽量使用const说明常量数据,对于宏定义的常数,必须指出其类型。

正例:

const int MAX_COUNT = 1000;

#define MAX_COUNT (int)1000

反例:

#define MAX_COUNT 1000

  1. 函数的命名统一采用有意义的单词组成,并且首字母大写,这类似与微软MSDN中函数命名规则,好记也好看。

例如:SetValue(int iWidth,int iHeight)

{

}

  1. 类名采用大小写结合的方法。在构成类名的单词之间不用下划线,类名在开头加上C,类的成员变量统一在前面加m_前缀。

例如:

class ClanglibVocabulary; // 类名以’C’开头

void Object::SetValue(int iWidth,int iHeight)

{

m_iWidth = iWidth; //类的成员变量统一在前面加m_前缀

m_iHeight = iHeight;

}

  1. 定义指针类型的变量,统一将 * 应该放在变量前。

正例:

float *pfBuffer; // 这样子声明指针变量,清晰明了

反例:

float* pfBuffer; // 这样子声明指针变量,不太直观

  1. 严禁使用未经初始化的变量作为右值。

说明:特别是在C/C++中引用未经赋值的指针,经常会引起系统崩溃。

5、函数、过程

  1. 防止将函数的参数作为工作变量。

说明:将函数的参数作为工作变量,有可能错误地改变参数内容,所以很危险。对必须改变的参数,最好先用局部变量代之,最后再将该局部变量的内容赋给该参数。

示例:下函数的实现不太好。

void sum_data( unsigned int num, int *data, int *sum )

{

unsigned int count;



\*sum = 0;

for (count = 0; count \< num; count++)

{

    \*sum  += data\[count\]; // sum成了工作变量,不太好。

}

}

若改为如下,则更好些。

void sum_data( unsigned int num, int *data, int *sum )

{

unsigned int count ;

int sum\_temp;



sum\_temp = 0;

for (count = 0; count \< num; count ++)

{

    sum\_temp  += data\[count\];

}



\*sum = sum\_temp;

}

  1. 为简单功能编写函数。

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

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

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);

  1. 避免使用无意义或含义不清的动词为函数命名。

说明:避免用含义不清的动词如process、handle等为函数命名,因为这些动词并没有说明要具体做什么。

  1. 如果参数是指针,且仅作输入用,则应该在类型前面加const。

说明:这样子,可以防止该指针在函数体内被意外修改。

正例:

int GetStrLen(const char *pcString); // 指明该指针只允许读,不可以改变其值

  1. 避免函数有太多的参数,参数个数尽量控制在5个以内。

说明:如果参数太多,在使用时很容易将参数类型或顺序搞错,而且调用的时候也不方便。如果参数的确比较多,而且输入的参数互相之间的关系比较紧密,不妨把这些参数定义成一个结构体,然后把结构体的指针当成参数输入。

6、可靠性

  1. 编程时,要防止差1错误。

说明:此类错误一般是由于把”<=”误写成”<“或”>=”误写成”>“等造成的,由此引起的后果,很多情况下是很严重的,所以编程时,一定要在这些地方小心。当编完程序后,应对这些操作符进行彻底检查。

  1. 防止内存操作越界。对变量进行赋值时,必须对其值进行合法性检查,防止越界等现象发生。

说明:内存操作主要是指对数组、指针、内存地址等的操作。内存操作越界是软件系统主要错误之一,后果往往非常严重,所以当我们进行这些操作时一定要仔细小心。

尤其对全局变量赋值时,应进行合法性检查,以提高代码的可靠性、稳定性。

例如灰度直方图的统计

BYTE *pGray[255]

int grayVal;

grayVal = .….

int num = pGray[grayVal];

  1. 有可能的话,if语句尽量加上else分支,对没有else分支的语句要小心对待;switch语句必须有default分支。
  1. 注意变量取值范围。

示例:如下程序将造成变量下溢。

unsigned char size ;

while (size-- >= 0) // 将出现下溢

{

\... // program code

}

当size等于0时,再减1不会小于0,而是0xFF,故程序是一个死循环。应如下修改。

signed char size; // 从unsigned char 改为signed char

while (size-- >= 0)

{

\... // program code

}

  1. 使用变量时要注意其边界值的情况。

说明:如C语言中字符型变量,有效值范围为-128到127。故以下表达式的计算存在一定风险。

示例:

char chr = 127;

int sum = 200;

chr += 1; // 127为chr的边界值,再加1将使chr上溢到-128,而不是128。

sum += chr; // 故sum的结果不是328,而是72。

// 若chr与sum为同一种类型,或表达式按如下方式书写,可能会好些。

sum = sum + chr + 1;

  1. 必须对动态申请的内存做有效性检查,并进行初始化;动态内存的释放必须和分配成对存在,以防止内存泄露,释放后内存指针要置为NULL。

说明:指针释放后,该指针可能还是指向原有的内存块,可能不是,但它已经变成了一个野指针,一般用户不会对它再操作,但用户失误情况下对它的操作可能导致程序崩溃。

示例:

MemoryFunction(void)

{

Unsigned char *pucBuffer=NULL;

pucBuffer = GetBuffer(sizeof(DWORD));

if( NULL != pucBuffer) // 申请的内存指针必须进行有效性验证

{

// 申请的内存使用前必须进行初始化

memset(pucBuffer,0xFF,sizeof(DWORD));

}

FreeBuffer(pucBuffer); // 申请的内存使用完毕必须释放

pucBuffer = NULL; // 申请的内存释放后指针置为空

}

If( p != NULL)

{

Delete [] p

P = NULL

}

P = new int [255];

  1. 指针类型变量必须初始化为NULL。

  2. 尽量用乘法或其它方法代替除法,特别是浮点运算中的除法

说明:浮点运算除法要占用较多CPU资源。

示例:如下表达式运算可能要占较多CPU资源。

#define PAI 3.1416

radius = circle_length / (2 * PAI);

应如下把浮点除法改为浮点乘法

#define PAI_RECIPROCAL (1 / 3.1416 ) // 编译器编译时,将生成具体浮点数

radius = circle_length * PAI_RECIPROCAL / 2;

7、宏

  1. 用宏定义表达式时,要使用完备的括号。

示例:如下定义的宏都存在一定的风险。

#define RECTANGLE_AREA ( a, b ) a * b

#define RECTANGLE_AREA ( a, b ) (a * b)

#define RECTANGLE_AREA ( a, b ) (a) * (b)

正确的定义应为:

#define RECTANGLE_AREA ( a, b ) ((a) * (b))

#define PACC 100+200

int num = PACC *10;

  1. 使用宏时,不允许参数发生变化。

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

#define SQUARE( a ) ((a) * (a))

int a = 5;

int b;

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

正确的用法是:

b = SQUARE( a );

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

8、参考文献(简单罗列)

  1. 《软件编程规范》,[http://www.doc88.com/p-67549398339.html]{.underline}

  2. 《华为编程总规则》,[http://wenku.baidu.com/view/1be045d249649b6648d7476d.html]{.underline}

  3. 《C++软件开发编程规范》,[http://download.csdn.net/download/sunyanjie/3054454]{.underline}

  4. 《华为软件编程规范》,

[http://www.eefocus.com/guobinggen/blog/2012-05/209725_e8029.html]{.underline}

9、软件容错方法

软件容错的方法,这里主要列举了两种:

  1. 用断言ASSERT_REPORT( expr );

  2. C++语言中用 try_except 处理法、try_finally中止法

  1. 用断言ASSERT_REPORT( expr );

在使用的断言的过程中会有一些我们应该注意的事项和养成一些良好的习惯,如:

  1. 每个ASSERT_REPORT只检验一个条件,因为同时检验多个条件时,如果断言失败,我们就无法直观的判断是哪个条件失败

例如:

不好 : ASSERT_REPORT (nOffset>=0 && nOffset+nSize<=m_nInfomationSize);

好 : ASSERT_REPORT (nOffset >= 0);
ASSERT_REPORT (nOffset+nSize <= m_nInfomationSize);

  1. ASSERT_REPORT和后面的语句应空一行,以形成逻辑和视觉上的一致感,也算是一种良好的编程习惯吧,让编写的代码有一种视觉上的美感

  2. 有的地方,ASSERT_REPORT不能代替条件过滤

  3. 不能使用改变环境的语句,因为ASSERT_REPORT只在DEBUG个生效,如果这么做,会使用程序在真正运行时遇到问题
    例如:

错误: ASSERT_REPORT (i++ < 100)
这是因为如果出错,比如在执行之前i=100,那么这条语句就不会执行,那么i++这条命令就没有执行。

正确: ASSERT_REPORT (i < 100)
i++;

  1. 注意事项:

使用ASSERT_REPORT的缺点是,频繁的调用会极大的影响程序的性能,增加额外的开销。所以这里的做法是:在Debug下编译的,自动开启断言;在Release下编译的,自动关闭断言。(即:ASSERT_REPORT只有在Debug版本中才有效,如果编译为Release版本则被忽略掉。)

重新写的断言函数ASSERT_REPORT( expr )实现的功能是:

程序出错时:列出在哪个程序、哪个文件、哪个函数、哪条表达式、哪行,可以快速定位那里出错,方便调试。

效果如下:

{width=”5.166666666666667in” height=”4.604166666666667in”}

具体使用方法:

例子1:

#include "ASSERT_REPORT.H"

void CtestAssertReportDlg::OnBnClickedButton1()

{

int *p;

p=NULL;

ASSERT_REPORT(p!=NULL);

}

例子2:在函数开始处检验传入参数的合法性

int resetBufferSize(int nNewSize)
{
//功能:改变缓冲区大小,
//参数:nNewSize 缓冲区新长度
//返回值:缓冲区当前长度
//说明:保持原信息内容不变 nNewSize<=0表示清除缓冲区
ASSERT_REPORT (nNewSize >= 0);
ASSERT_REPORT (nNewSize <= MAX_BUFFER_SIZE);

...
}

欢迎关注我的其它发布渠道