关于宏的正确用法和错误用法

来源于microchip资深工程师的网络研讨会


     关于C的预处理和它对宏定义的预处理,我们来看一些宏的正确和错误的拓展,以及怎样拓展他们,所有的规范都是建立在ANSI C编译器基础上的。

1.    简单的宏处理:


     #define  name  “replacement text”

     示例用法:

     #define START  20

        foobar = START;

replacement text”可以被其他的宏定义展开

例如:   

         #define LIMIT  100

         #define PREF   LIMIT

         setting = PREF;

这种宏展开可以拓展至没有宏定义为止


2.    防止重复定义的宏

例如:

#ifndef LCDH
// header file contents go here
// now stop this being included again
#define LCDH
#endif

此种使用的作用范围是从定义到模块结束,或者遇到#undef


3.带参数的宏定义


#define  name(argument-list)  replacement text


参数会替换掉“replacement text”里的对应项

示例用法:

#define DIFF(a, b)  ((a)>=(b) ? (a)-(b) : -1)

result = DIFF(input, 6);

展开后是:

result = ((input)>=(6) ? (input)-(6) : -1);

预处理器能够替换一个或者更多的变量参数,实现更加复杂的替换。为了防止产生非预料的结果,每个替换的变量都要加上括号。

4.字符串替换

例如:

#define PRODUCT(year)  "Matrix" #year

const char * productName = PRODUCT(2000);

展开后:

const char * productName = "Matrix" "2000";

最终变成:

const char * productName = "Matrix2000";

当宏定义中有变量参数时,你就能够使用一些特殊的操作符。其中一个就是#操作符,它可以把变量转换成为c字符串。在这个例子里,经过预处理,变量productName将会指向字符串“Matrix2000”,在扩展中,两个符号#year被变量替换,并且产生的字符串和“Matrix”连接。

5.连接变量

例如:

#define BLATCH(bit)   LATB##bit
BLATCH(4) = 1;

替换成为:

LATB4 = 1;

另一个宏操作符就是“##”,可以让你连接任意的两个符号,而不仅仅是字符串。在这个例子中,操作符##用来连接符号LATB和bit变量。当产生替换时,它就会扩展成LATB4,这就是PIC处理器的latch B的第四位端口。

这个功能很有用,但是在变量本身是其他需要预处理器扩展的宏时,会有一个潜在的问题,我们看下面:

#define BLATCH(bit)   LATB##bit

#define MOTOR  4

BLATCH(MOTOR) = 1;

经过预处理器

会扩展成为:LATBMOTOR = 1;

这显然不是我门想要的结果

问题的根源是,当宏变量进行替换时,宏变量不会被优先扩展。预处理器的定义是,当只有宏变量不在#,##之前或者不在##之后的时候才能被扩展,所以当“MOTOR插入到“BLATCH”中时,他不会被进一步扩展,因为它临近着##操作符。

幸运的时,我们找到了一个方法实现这种扩展:

可以分两步,来进行:

#define PASTE(a, b)    a##b

#define BLATCH(bit)   PASTE(LATB, bit)


#define MOTOR  4


BLATCH(MOTOR) = 1;

经过预处理器:

LATB4 = 1;

第二个宏实现变量的扩展,第二个宏实现了连接,这就实现了我们要的功能。


我们来看最后一个复杂一些的例子:

#define PASTE2(a,b)   a##b

#define PASTE(a,b)    PASTE2(a,b)


#ifdef  PIC18  // set for PIC18 devices
#define IO            LAT
#else
#define IO            PORT
#endif
#define PIN           4
#define RDPORT        PASTE(PORT, PIN)
#define RDLATCH       PASTE(IO, PIN)     
#define IOREAD(loc)   PASTE(RD, loc)


使用:

result = IOREAD(LATCH);

当我们看懂这个例子的时候,会觉得很有成就感吧,但当你认为这段代码会工作的很顺利时,你将会发现编译器错误或者其他的不良后果。

   最后的结果是:result = PASTE(LAT, 4);

并不是我们想要的,究其原因就是每个宏扩展在一个表达式里只能实现一次,具体过程是这样的:


IOREAD(LATCH)
  -> PASTE(RD, LATCH)
    -> RDLATCH
      -> PASTE(IO, PIN)
        -> PASTE(LAT, 4); ✘

在进行PASTE第二次扩展时,预处理器不会进行扩展。


解决办法就是另外再写一个,和PASTE同样功能,但是名字不一养的宏来使用。





你可能感兴趣的:(关于宏的正确用法和错误用法)