宏的使用及小技巧

 
1.        防止头文件中被多次include
#ifndef _MY_H_FILE_
#define _MY_H_FILE_
#endif // _MY_H_FILE_
2.        条件编译
#ifdef CONDITION
#endif // CONDITION
 
#if Cond >= 10
#elif Cond > 6
#endif
3.        log输出时,自动加入代码行数,文件名等
#define log( fmt , … )    OutputLog( __FILE__ , __LINE__ , fmt , __VA_ARGS__ )
void OutputLog( const char * file , int line , const char * fmt , … )
{
    const char * logfmtprefix = “%s(%d) -- ” ;
    va_list args ;
    va_start( args , fmt ) ;
   
    printf(logfmtprefix, file , line) ;
     vprintf ( fmt , args) ;
}
用到了C99的可变参数宏,同可变参数函数一样,用…来表示剩余的参数。用__VA_ARGS__来代替实际的参数。
注:gcc支持可变参数宏。VS2005之前的版本不支持该语法,VS2005开始支持该语法。
结合2的功能,可以实现在debug模式下输出log,而在release模式下,空操作。
#ifdef _DEBUG_
#define log( fmt , … ) OutputLog( __FILE__ , __LINE__ , fmt , __VA_ARGS__ )
#else
    #define log( fmt , …) 
#endif // _DEBUG_
 
4.        替换为逻辑语句(较函数调用效率更高,代码size增大)
#define init_list(list) do { list->next = NULL ; /
                         list->prev = NULL ; /
                     while(0)
使用do while的原因为防止出现编译错误。比如如下代码:
if ( list )
    init_list( list ) ;
else
    list = (LIST*)malloc( sizeof(*list) ) ;
当没有使用do while是,它被预处理为:
if ( list )
    list->next = NULL ;
next->prev = NULL ;
else
    list = (LIST*)malloc( sizeof(*list) ) ;
很明显,将出现编译错误。
而使用do while之后,它被预处理为:
if ( list )
    do {
        list->next = NULL ;
next->prev = NULL ;
        while(0) ;
else
    list = (LIST*)malloc( sizeof(*list) ) ;
它完全符合标准C的语法。
5.        Stringizing Operator (#)
#define stringer( x ) printf( #x "/n")
Stringer(“this is a string”);
将在console输出this is a string
6.        Token-Pasting Operator (##)
     #define paster( n ) printf( "token" #n " = %d", token##n )
int token9 = 9;
paster( 9 ); 将被预处理为:
printf( "token" "9" " = %d", token9 );
#n    → “9”
token##n → token9
7.        代码生成
看看MFC的Message Map宏,ATL的Interface Map宏。他们简直就是代码生成器。
-------------------------------------------------------------------------------------------------------------------------
 
1.        scanf的妙用
我们先看看MSDN的描述:
Format Specification Fields: scanf and wscanf Functions 
The information here applies to the entire scanf family of functions, including the secure versions and describes the symbols used to tell the scanf functions how to parse the input stream, such as the input stream stdin for scanf, into values that are inserted into program variables.
A format specification has the following form:
%[*] [width] [{h | l | ll | I64 | L}]type
The format argument specifies the interpretation of the input and can contain one or more of the following:
l         White-space characters: blank (' '); tab ('/t'); or newline ('/n'). A white-space character causes scanf to read, but not store, all consecutive white-space characters in the input up to the next non–white-space character. One white-space character in the format matches any number (including 0) and combination of white-space characters in the input.
l         Non–white-space characters, except for the percent sign (%). A non–white-space character causes scanf to read, but not store, a matching non–white-space character. If the next character in the input stream does not match, scanf terminates.
l         Format specifications, introduced by the percent sign (%). A format specification causes scanf to read and convert characters in the input into values of a specified type. The value is assigned to an argument in the argument list.
The format is read from left to right. Characters outside format specifications are expected to match the sequence of characters in the input stream; the matching characters in the input stream are scanned but not stored. If a character in the input stream conflicts with the format specification, scanf terminates, and the character is left in the input stream as if it had not been read.
When the first format specification is encountered, the value of the first input field is converted according to this specification and stored in the location that is specified by the first argument. The second format specification causes the second input field to be converted and stored in the second argument, and so on through the end of the format string.
An input field is defined as all characters up to the first white-space character (space, tab, or newline), or up to the first character that cannot be converted according to the format specification, or until the field width (if specified) is reached. If there are too many arguments for the given specifications, the extra arguments are evaluated but ignored. The results are unpredictable if there are not enough arguments for the format specification.
Each field of the format specification is a single character or a number signifying a particular format option. The type character, which appears after the last optional format field, determines whether the input field is interpreted as a character, a string, or a number.
The simplest format specification contains only the percent sign and a type character (for example, %s). If a percent sign (%) is followed by a character that has no meaning as a format-control character, that character and the following characters (up to the next percent sign) are treated as an ordinary sequence of characters, that is, a sequence of characters that must match the input. For example, to specify that a percent-sign character is to be input, use %%.
An asterisk (*) following the percent sign suppresses assignment of the next input field, which is interpreted as a field of the specified type. The field is scanned but not stored.
To read strings not delimited by space characters, a set of characters in brackets ([ ]) can be substituted for the s (string) type character. The corresponding input field is read up to the first character that does not appear in the bracketed character set. If the first character in the set is a caret (^), the effect is reversed: The input field is read up to the first character that does appear in the rest of the character set.
Note that %[a-z] and %[z-a] are interpreted as equivalent to %[abcde...z]. This is a common scanf function extension, but note that the ANSI standard does not require it.
然后看看CRT的代码注释:
%[abcd]
       matches strings containing only <<a>>, <<b>>, <<c>>, and <<d>>.
%[^abcd]
       matches strings containing any characters except <<a>>, <<b>>,
                     <<c>>, or <<d>>
%[A-DW-Z]
       matches strings containing <<A>>, <<B>>, <<C>>, <<D>>, <<W>>,
                     <<X>>, <<Y>>, <<Z>>
%[z-a]
       matches the characters <<z>>, <<->>, and <<a>>
是否有些许正则表达式的味道呢,只不过支持的是最简单的regexp。
下面看一个例子:
int main( void )
{
   char * str = "0x40,234,ID=abcXX, NAME=john," ;
   int id ;
   char name[24] = { 0 } ;
  
   sscanf( str , "%x,%*d,%*[^,],%*[^=]=%[^,]" , &id , name );
   printf("id= %d, name=%s/n" , id , name ) ;
}
%x       -- 提取0x40,并赋值给id
%*d      -- 提取234后,并忽略该值,*的作用
%*[^,]    -- 提取ID=abcXX,并忽略该值
%*[^=] --    提取NAME,并忽略该值
%[^,]     -- 提取john,并赋值给name
程序运行结束后,输出id= 64, name=john
2.        表驱动和函数指针
条件 1
条件 2
处理操作
TRUE
TRUE
pfnProcess1
TRUE
FALSE
pfnProcess2
FALSE
TRUE
pfnProcess3
FALSE
FALSE
pfnProcess4
通过函数指针抽象出操作,然后再提供不同的实现。
也支持将注册动作和执行动作分离开来(Command Pattern)。比如Linux的bottom half处理机制,现在已经更新为tasklet。在top half中利用tasklet_schedule函数注册action,在处理完毕中断后,执行注册的action。
通过组合各种操作,来抽象出某类相关联的操作。以屏蔽不同实现的差异性。
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值