宏的基本使用规范

来源:互联网 发布:asp塔配哪种网站数据库 编辑:程序博客网 时间:2024/06/04 18:48

宏的命名

说明:全大写字母,下划线分隔。可加上简短的模块名作为前缀。

原因:名称清晰,符合通常的规范,避免宏定义重复,防止冲突。

 

严禁在宏定义中进行数组、结构、字符串变量的定义

原因:函数中对宏的多次引用,会导致实际局部变量空间成倍放大

 

按照宏的功能、模块进行集中定义

说明:在一个地方将常量数值定义为宏,其他地方通过引用该宏,生成自己模块的宏。严禁相同含义的常量数值,在不同的地方定义为不同的宏,即使数值相同也是不允许的(维护修改后,极易遗漏,造成代码隐患)。

原因:杜绝同类的宏,定义在不同的地方,导致维护困难

 

宏的名字全部采用大写字母与下划线、数字,且在前部包含模块、功能的信息

 

代码中所有非0,1的常量值,用宏进行代替(改进见下一条)

代码中所有非-1,0,1的数值,尽量用一个有意义的标识符代替

例子:/*宏定义法*/

     #define PIE 3.14

     /*常量定义法*/

     const float PIE = 3.14;

     /*枚举定义法*/

     enum

     {

         PIE = 3

     };

原因:1. 方便阅读、理解代码;

     2. 修改、维护方便,只需改动定义即可,不需在代码中改动所有直接使用值的地方。

     3. 运行效率不变

     

用常量适当替代(类似功能的)宏

例子:#define PIE 3.14

     const float PIE = 3.14;

原因:1. 宏是在预编译时用定义值全程替换宏名的,这样在编译时编译器不知道宏名,报错不易理解;

     2. 跟踪调试时显示值,而不是宏名;

     3. 宏没有类型,不能做类型检查,不安全;

     4. 宏没有作用域;

     5. 常量和宏的效率同样高;

 

若宏值多于一项,必须使用括号

例子:#define ERROR_LENGTH (100+1)

原因:当宏用在表达式中时,防止运算符优先级导致的计算混乱

 

不要用分号结束宏定义

例子:#define MAX_CHANNEL_NUM    16;

原因:可能导致编译出错

 

用inline函数代替(类似功能的)宏函数

说明:不是绝对的!某些宏函数的用法独特,inline函数取代不了。当不想指明或不能指明参数类型时,宏函数更合适。

原因:1. 宏函数在预编译时处理,编译出错信息不易理解;

     2. 宏函数本身无法单步跟踪调试,但某些编译器(为了调试需要)可以将inline函数转成普通函数;

     3. 宏函数的入参没有类型,可以是任何东西,有时不安全;

     4. inline函数会在目标代码中展开,和宏的效率一样高;

 

函数宏的每个参数都要括起来

例子:#define WEEKS_TO_DAYS(w)  (w *7)

     #define WEEKS_TO_DAYS(w)  ((w) *7)

 

不带参数的宏函数也要定义成函数形式

例子:#define HELLO( )  printf(“Hello.”)

     #define HELLO    printf(“Hello.”)

原因:括号会暗示阅读代码者此宏是一个函数。

 

自定义数据类型,用typedef,而不是宏

例子:#define LONG_PTR    long*

     LONG_PTR    pA, pB;

     本意是想定义两个指针,但展开后相当于“long* pA, pB;”,所以pB不是指针,而是long型变量。用typedef消除宏的弊病:

     typedef long* LongPtr_T;

     LongPtr_T pA, pB;

原因:宏的最大特点(弱点)就是在预编译时做简单替换,没有任何语法约束在里面。看似被宏归为一类的一串语法元素,其实是一盘散沙,和宏之外的其他元素没有任何差别。这是导致期望和实际效果不符的主要原因。

 

使用宏防范头文件被多次引用

例子:#ifndef _OAM_MACRO_H

#define _OAM_MACRO_H

#endif

说明:1. 此类宏的命名,基本上可以和头文件的文件名相同。

     2. 此类宏如果重名,会造成严重后果,为了保证唯一性,长一点无妨,不要缩写。

不要用宏改写语言

例子:#define FOREVER for ( ; ; )

     #define BEGIN {

#define END   }

原因:C语言有完善且众所周知的语法。试图将其改变成类似于其他语言的形式,会使代码阅读者混淆,难于理解。

 

编译宏一定要放在句首,不缩进

例子:……

       case CMD_MODE_NPCIX_WAN_MODE:

#if defined(MP)

              caseCMD_MODE_VBUI_INTERFACE:

#endif

#if INSTALL_EVENT_LINKAGE

       case CMD_EXEC_EVT_LIST:

#endif

       case CMD_MODE_TIMERANGE:

               pOamMsg->out_index =pLinkState->out_index;

               break;

原因:便于阅读、理解实际生效的代码。

 

用{ }或do{ }while(0)将函数宏的函数体括起来

原因:尽量保证宏函数作为一条独立的语句,不干扰别人,也不受别人干扰

说明:宏定义中使用do{}while(0)的原因及好处:

格式:#defineMACRO_NAME(para) do{…} while(0)

1. 避免空的宏定义产生warning:

#define foo( ) do{}while(0)

 

2. 存在一个独立的block,可以进行变量定义,实现比较复杂的逻辑处理。

 

3. 如果宏出现在判断语句之后,可以保证作为一个整体来实现:

#define foo(x) \

action1( ); \

action2( );

 

在以下情况下:

if (NULL == pPointer)

   foo( );

  

就会出现action1和action2不会同时被执行的情况,这显然不是程序设计的原始目的。

 

4. 以上的3种情况,用单独的{}也可以实现,但是为什么一定要一个do{}while(0)呢?

看以下代码:

#define SWITCH(x, y) {int tmp;tmp="x"; x=y; y=tmp;}

if (x > y)

    SWITCH(x, y);

else      //error, parse error before else

    OtherAction();

 

在把宏引入代码中,会多出一个分号,从而会报错。

 

使用do{...}while(0) 把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{...}while(0)这种无用的循环并优化,所以使用这种方法不会导致程序的性能降低。

 

 

0 0