| 碧海潮生's profile碧海潮生的小屋PhotosBlogLists | Help |
|
May 25 设计笔记——宏定义(下)8 根据位模式构筑图形 这是《C Expert Programming》上的一个例子。 这是一个惊人的#define定义的优雅集合, 允许程序建立常量使它们看上去像是屏幕上的图形。 #define X )*2+1 #define _ )*2 #define s ((((((((((((((((0 /* 用于建立16位宽的图形 */ 定义了它们之后,只要画所需要的图标或者图形等,程序会自动创建它们的十六进制模式。 使用这些宏定义,程序的自描述能力大大加强: static unsigned short stopwatch[] = { s _ _ _ _ _ X X X X X _ _ _ X X _ , s _ _ _ X X X X X X X X X _ X X X , s _ _ X X X _ _ _ _ _ X X X _ X X , s _ X X _ _ _ _ _ _ _ _ _ X X _ _ , s _ X X _ _ _ _ _ _ _ _ _ X X _ _ , s X X _ _ _ _ _ _ _ _ _ _ _ X X _ , s X X _ _ _ _ _ _ _ _ _ _ _ X X _ , s X X _ X X X X X _ _ _ _ _ X X _ , s X X _ _ _ _ _ X _ _ _ _ _ X X _ , s X X _ _ _ _ _ X _ _ _ _ _ X X _ , s _ X X _ _ _ _ X _ _ _ _ X X _ _ , s _ X X _ _ _ _ X _ _ _ _ X X _ _ , s _ _ X X X _ _ _ _ _ X X X _ _ _ , s _ _ _ X X X X X X X X X _ _ _ _ , s _ _ _ _ _ X X X X X _ _ _ _ _ _ , s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ }; C语言具有八进制、十进制和十六进制常量,但没有二进制常量, 否则的话倒是一种更为简单的绘制图形模式的方法。 最后,千万不要忘了在绘图结束后清除这些宏定义, 否这很可能会给你后面的代码带来不可预测的后果。 9 一个奇技淫巧 这是《C Interfaces and Implementations》上的一个例子。 在CII第四章中,对于RETURN的宏定义是: #define RETURN switch(Exception_stack = Exception_stack->prev, 0) default: return 放在如下的代码中: TRY /* do something */ if (failed) RETURN 1; EXCETPT (Allocation_Failed) /* Handle allocation fail */ END_TRY 展开之后就是:(暂不考虑TRY的宏定义) TRY /* do something */ if (failed) switch(Exception_stack = Exception_stack->prev, 0) default: return 1; EXCEPT (Allocation_Failed) /* Handle allocation fail */ END_TRY 可以看到,这个宏的唯一目的,就是在return 1之前, 执行Exception_stack = Exception_stack->prev语句。 仔细考虑之后可以发现,C语言中其他的结构都不能满足这一条件。 如果使用#define RETURN if(Exception_stack = Exception_stack->prev, 1) return 则在 if (failed) RETURN 1; else ... 语句中,会导致else的误匹配。 10 定义多行的宏 字符'\'可用于对一些字符进行“转义”,包括newline(指回车符)。 被转义的newline在逻辑上把下一行当作当前行的延续,它可用于连续多行的宏定义。 如: #define wdt_init() \ WDTRST = 0x1E; \ WDTRST = 0xE1 则,程序中的wdt_init();将展开为: WDTRST = 0x1E; WDTRST = 0xE1; 但这样的定义在if-else语句中展开时将产生错误。 if (condition) wdt_init(); else statement; 展开后成为: if (condition) WDTRST = 0x1E; WDTRST = 0xE1; else statement; 导致else失配错误。 通常的解决方法是用do{ }while(0)结构将多行语句包起来。如: #define wdt_init() do { \ WDTRST = 0x1E; \ WDTRST = 0xE1; \ } while (0) 这样,上面的if-else结构展开后就成为了: if (condition) do { WDTRST = 0x1E; WDTRST = 0xE1; } while (0); else statement; 11 将函数化为宏的技巧 在嵌入式系统中,将一些简单的函数用宏代替是非常经济和实惠的。 把函数化为宏,其基本原则是将宏实现成表达式。 上面的那个多行的宏,实际上完全可以用逗号表达式实现: #define wdt_init() (WDTRST = 0x1E, WDTRST = 0xE1) 逗号运算符在所有运算符中优先级最低。 其结合性从左至右,因此又称为顺序求值运算符。 逗号表达式的值是最右边操作数的值,这也提供了一种模拟返回值的方法。 如函数: uint8 flash_verify_page(void) { ECON = 0x04; return !ECON; } 可以化为以下的宏形式: #define flash_verify_page() (ECON = 0x04, !ECON) 而调用方代码可以是: rc = flash_verify_page(); 也可以作为if语句的条件表达式: if (!flash_verify_page()) return 0; 另外,一些简单的选择语句可以用逻辑运算符和?:运算符实现。如: void timer0_set_gate(uint8 val) { if (val) TMOD |= 0x80; } 可以化为以下的宏形式: #define timer0_set_gate(val) ((val) && (TMOD |= 0x08)) 这个实现利用了&&运算符的“短路原则”,只有当val为真时,才会执行TMOD |= 0x08。 void tic_set_tien(uint8 val) { if (val) TIMECON |= 0x02; else TIMECON &= 0xFD; } 则可以化为以下的宏形式: #define tic_set_tien(val) ((val) ? (TIMECON |= 0x02) : (TIMECON &= 0xFD)) 这里,唯一的三目运算符?:也是具有右结合性的。 顺便强调一下,C语言中,“有些运算符的优先级是错误的”, “当按照常规方式使用时,可能引起误会的任何运算符”就是存在错误优先级的运算符。 表达式中如果有布尔操作、算术运算、位操作等混合计算,始终应该在适当的地方加上括号。 C语言中记住两个优先级就够了:乘法和除法先于加法和减法,在涉及其他操作符时一律加上括号。 12 结语 忠告:宏就像C语言工程师手中的一把双刃剑,如果能很好的利用它, 它就会死心塌地的为你服务,不过可不要把自己的手给弄破了。 参考文献: [1] 高质量程序设计指南——C/C++语言,林锐 [2] C Expert Programming,Peter Van Der Linden [3] C Interfaces and Implementations,David R. Hanson [4] ANSI C中取得结构体字段偏移值的惯用法,http://blog.csdn.net/soloist/archive/2005/01/18/258654.aspx [5] C语言宏定义中的一个奇技淫巧,http://blog.csdn.net/myan/archive/2004/04/27/542.aspx [6] C语言宏定义技巧,http://blog.csdn.net/goodluckyxl/archive/2006/08/11/1050992.aspx [7] C宏——智者的利刃,愚者的恶梦!,http://www.vckbase.net/document/viewdoc/?id=1454 TrackbacksThe trackback URL for this entry is: http://jx-kingwei.spaces.live.com/blog/cns!F7A152EB74B9576E!1500.trak Weblogs that reference this entry
|
|
|