C/C++宏的使用:
1. 防止多重包含
2. 条件编译
3. 定义字面值常量
4. 定义为函数
5. 可变参数宏
6. 宏组合
6.1 一般用法
6.2 当宏参数是另一个宏的时候
6.2.1 非'#'和'##'的情况
6.2.2 当有'#'或'##'的时候
6.3 '#'和'##'的一些应用特例
6.3.1 合并匿名变量名
6.3.2 填充结构
6.3.3 记录文件名
6.3.4 得到一个数值类型所对应的字符串缓冲大小
7. 其他使用例子
7.1 得到指定地址上的一个字节或字
7.2 求最大值和最小值
7.3 得到一个field在结构体(struct)中的偏移量
7.4 得到一个结构体中field所占用的字节数
7.5 按照LSB格式把两个字节转化为一个Word
7.6 按照LSB格式把一个Word转化为两个字节
7.7 得到一个变量的地址(word宽度)
7.8 得到一个字的高位和低位字节
7.9 返回一个比X大的最接近的8的倍数
7.10 将一个字母转换为大写
7.11 判断字符是不是10进值的数字
7.12 判断字符是不是16进值的数字
7.13 防止溢出的一个方法
7.14 返回数组元素的个数
7.15 对于IO空间映射在存储空间的结构,输入输出处理
1. 防止多重包含
防止头文件多重包含:
如下
CODE:
#ifndef MAIN_H_
#define MAIN_H_
其它内容
#endif
作用就是阻止这个头文件被多次include。多次include就会出现重复的定义情况,所以需要在每个头文件中都使用这个定义。
2. 条件编译
#ifdef _DEBUG
printf("this debug info/n");
#endif
如果没有定义_DEBUG宏,那么上面那一行是不会编译进去。但是定义了_DEBUG后,上面那行就会编译进去。
#ifdef _M_IX86
#elif defined _M_MRX000
#endif
3. 定义字面值常量
方便修改,尽量做到修改地方少。
#define PRINT_STR "你好"
main()
{
printf(PRINT_STR);
return 0;
}
4. 定义为函数
#ifdef _DEBUG
#define A(x) a(x)
#else
#define A(x) b(x)
#endif
int a(int x)
{
return x+1;
}
int b(int x)
{
return x+100;
}
int main()
{
printf ("A(10) value is %d",A(10));
return 0;
}
其实也可以定义成#define A a
但是定义成A(x)后只有A后面带一个(x)类型的编译器才会执行替换,比较安全,可以保证只替换函数而不替换变量。
5. 可变参数宏
有些时候定义一个宏来代替某个函数,但是这个函数是可变参数的话,那就需要考虑办法了
定义方法如下
#include <iostream>
using namespace std;
#define PRINT(...) cout<<(__VA_ARGS__)
#define PRINTC(...) printf(__VA_ARGS__)
int _tmain(int argc, _TCHAR* argv[])
{
//C++6.0不可运行
PRINT("FLY编译例子");
PRINT(endl);
PRINTC("%d %s %s",1,"吃饭了吗 smile MM:)","/n");
return 0;
}
6. 宏组合
也就是##和#的用法
##是连接符号,连接两个宏
#是把名字代替成字符串
6.1 一般用法
#define s5(a) supper_##a
#include <stdio.h>
void supper_printf(const char* p )
{
printf("this is supper printf:/n%s/n",p);
}
int main()
{
s5(printf)("hello owrld");
return 0;
}
#用法如下
#include <stdio.h>
#define s(p) #p
int main()
{
printf(s(p)"/n");
return 0;
}
6.2 当宏参数是另一个宏的时候
需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开。
6.2.1 非'#'和'##'的情况
#define TOW (2)
#define MUL(a,b) (a*b)
printf("%d*%d=%d/n", TOW, TOW, MUL(TOW,TOW));
这行的宏会被展开为:
printf("%d*%d=%d/n", (2), (2), ((2)*(2)));
MUL里的参数TOW会被展开为(2)。
6.2.2 当有'#'或'##'的时候
#define A (2)
#define STR(s) #s
#define CONS(a,b) int(a##e##b)
printf("int max: %s/n", STR(INT_MAX)); // INT_MAX #include<climits>
这行会被展开为:
printf("int max: %s/n", "INT_MAX");
printf("%s/n", CONS(A, A)); // compile error
这一行则是:
printf("%s/n", int(AeA));
INT_MAX和A都不会再被展开,然而解决这个问题的方法很简单。加多一层中间转换宏。
加这层宏的用意是把所有宏的参数在这层里全部展开,那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数。
#define A (2)
#define _STR(s) #s
#define STR(s) _STR(s) // 转换宏
#define _CONS(a,b) int(a##e##b)
#define CONS(a,b) _CONS(a,b) // 转换宏
printf("int max: %s/n", STR(INT_MAX)); // INT_MAX,int型的最大值,为一个变量#include<climits>
输出为: int max: 0x7fffffff
STR(INT_MAX)-->_STR(0x7fffffff) 然后再转换成字符串;
printf("%d/n", CONS(A, A));
输出为:200
CONS(A, A)-->_CONS((2), (2))-->int((2)e(2))
6.3 '#'和'##'的一些应用特例
6.3.1 合并匿名变量名
#define ___ANONYMOUS1(type, var, line) type var##line
#define __ANONYMOUS0(type, line) ___ANONYMOUS1(type, _anonymous, line)
#define ANONYMOUS(type) __ANONYMOUS0(type, __LINE__)
例:ANONYMOUS(static int); 即: static int _anonymous70; 70表示该行行号;
第一层:ANONYMOUS(static int); --> __ANONYMOUS0(static int, __LINE__);
第二层: --> ___ANONYMOUS1(static int, _anonymous, 70);
第三层: --> static int _anonymous70;
即每次只能解开当前层的宏,所以__LINE__在第二层才能被解开。
6.3.2 填充结构
#define FILL(a) {a, #a}
enum IDD{OPEN, CLOSE};
typedef struct MSG
{
IDD id;
const char * msg;
}MSG;
MSG _msg[] = {FILL(OPEN), FILL(CLOSE)};
相当于:
MSG _msg[] = {{OPEN, "OPEN"}, {CLOSE, "CLOSE"}};
6.3.3 记录文件名
#define _GET_FILE_NAME(f) #f
#define GET_FILE_NAME(f) _GET_FILE_NAME(f)
static char FILE_NAME[] = GET_FILE_NAME(__FILE__);
6.3.4 得到一个数值类型所对应的字符串缓冲大小
#define _TYPE_BUF_SIZE(type) sizeof #type
#define TYPE_BUF_SIZE(type) _TYPE_BUF_SIZE(type)
char buf[TYPE_BUF_SIZE(INT_MAX)];
--> char buf[_TYPE_BUF_SIZE(0x7fffffff)];
--> char buf[sizeof "0x7fffffff"];
这里相当于:
char buf[11];
7. 其他使用例子
7.1 得到指定地址上的一个字节或字
#define MEM_B( x ) ( *( (byte *) (x) ) )
#define MEM_W( x ) ( *( (word *) (x) ) )
7.2 求最大值和最小值
#define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )
#define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )
7.3 得到一个field在结构体(struct)中的偏移量
#define FPOS( type, field ) /
( (dword) &(( type *) 0)-> field )
7.4 得到一个结构体中field所占用的字节数
#define FSIZ( type, field ) sizeof( ((type *) 0)->field )
7.5 按照LSB格式把两个字节转化为一个Word
#define FLIPW( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] )
7.6 按照LSB格式把一个Word转化为两个字节
#define FLOPW( ray, val ) /
(ray)[0] = ((val) / 256); /
(ray)[1] = ((val) & 0xFF)
7.7 得到一个变量的地址(word宽度)
#define B_PTR( var ) ( (byte *) (void *) &(var) )
#define W_PTR( var ) ( (word *) (void *) &(var) )
7.8 得到一个字的高位和低位字节
#define WORD_LO(xxx) ((byte) ((word)(xxx) & 255))
#define WORD_HI(xxx) ((byte) ((word)(xxx) >> 8))
7.9 返回一个比X大的最接近的8的倍数
#define RND8( x ) ((((x) + 7) / 8 ) * 8 )
7.10 将一个字母转换为大写
#define UPCASE( c ) ( ((c) >= ''a'' && (c) <= ''z'') ? ((c) - 0x20) : (c) )
7.11 判断字符是不是10进值的数字
#define DECCHK( c ) ((c) >= ''0'' && (c) <= ''9'')
7.12 判断字符是不是16进值的数字
#define HEXCHK( c ) ( ((c) >= ''0'' && (c) <= ''9'') ||/
((c) >= ''A'' && (c) <= ''F'') ||/
((c) >= ''a'' && (c) <= ''f'') )
7.13 防止溢出的一个方法
#define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))
7.14 返回数组元素的个数
#define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) )
7.15 对于IO空间映射在存储空间的结构,输入输出处理
#define inp(port) (*((volatile byte *) (port)))
#define inpw(port) (*((volatile word *) (port)))
#define inpdw(port) (*((volatile dword *)(port)))
#define outp(port, val) (*((volatile byte *) (port)) = ((byte) (val)))
#define outpw(port, val) (*((volatile word *) (port)) = ((word) (val)))
#define outpdw(port, val) (*((volatile dword *) (port)) = ((dword) (val)))