嵌入式 > 技术百科 > 详情

如何避免【宏】的这7个误区?

发布时间:2023-10-23 发布时间:
|

语法错误

当使用参数调用宏时,会将参数替换为宏主体,并与其他输入文件一起检查结果,以进行更多的宏调用,可以将部分来自宏主体和部分自变量的宏调用组合在一起。例如,

#define twice(x) (2*(x))

#define call_with_1(x) x(1)

call_with_1 (twice)

//x=1

→ twice(1)

→ (2*(1))

宏定义不必带有括号,通过在宏主体中编写不平衡的开放括号,可以创建一个从宏主体内部开始但在宏主体外部结束的宏调用。例如,

#define strange(file) fprintf (file, "%s %d",

strange(stderr) p, 35)
    → fprintf (stderr, "%s %d", p, 35)

组合宏调用的功能可能会很有用,但是在宏主体中使用不平衡的开放括号只会造成混淆,应该避免。

运算符优先级问题

在大多数宏定义示例中,每次出现的宏参数名称都带有括号,并且另一对括号通常会包围整个宏定义,这是编写宏最好的方式。举个例子

#define ceil_div(x, y) (x + y - 1) / y

假定其用法如下:

a = ceil_div(b&c,sizeof(int));

拓展开是

a =(b&c + sizeof(int)-1)/ sizeof(int);

这没有达到我们的预期,C的运算符优先级规则使其等效于此,而我们想要的是:

a =(((b&c)+ sizeof(int)-1))/ sizeof(int);

如果我们将宏定义为

#define ceil_div(x,y)((x)+(y)-1)/(y)

可能导致另一种情况,sizeof ceil_div(1,2)是一个C表达式,可以计算ceil_div(1,2)类型的大小,它扩展为:

sizeof((1)+(2)-1)/(2)

这将采用整数的大小并将其除以2,而除法包含在内部的sizeof之外。所以整个宏定义的括号可防止此类问题。那么,下面是定义ceil_div的正确方法如下

#define ceil_div(x,y)((((x)+(y)-1)/(y))

吞噬分号

通常需要定义一个扩展为复合语句的宏。例如,考虑以下宏,该宏跨空格字符前进一个指针(参数p表示在何处查找):

#define SKIP_SPACES(p, limit)  

{ char *lim = (limit);         

 while (p < lim) {            

   if (*p++ != ' ') {         

     p--; break; }}}

该宏定义必须是单个逻辑行,严格来说,该调用扩展为复合语句,这是一个完整的语句,不需要用分号结束。

但是,由于它看起来像函数调用,因此,如果可以像使用函数调用一样使用它,则可以最大程度地减少混乱,然后再写一个分号,就像在SKIP_SPACES(p,lim)中一样。

这可能会在else语句之前出问题,因为分号实际上是空语句。假设你写

if (*p != 0)

 SKIP_SPACES (p, lim);

else …

在if条件和else条件之间存在两个语句(复合语句和null语句)使C代码无效。

怎么解决?我们可以使用do…while语句更改宏SKIP_SPACES的定义以解决此问题。方法如下:

#define SKIP_SPACES(p, limit)     

do { char *lim = (limit);         

    while (p < lim) {            

      if (*p++ != ' ') {         

        p--; break; }}}          

while (0)

SKIP_SPACES (p, lim);扩展为

do {…} while (0);

这是一个陈述,循环仅执行一次,而且大多数编译器不会为此生成任何额外的代码。


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
中芯国际与阿斯麦签订12亿美元大单