转载:C/C++语言宏定义技巧

记录一些C++中宏定义的使用技巧,转自CSND

1.防止一个头文件被重复包含

1
2
3
4
5
6
#ifndef COMDEF_H 
#define COMDEF_H

//头文件内容

#endif

2.重新定义一些类型,防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植。

1
2
3
4
5
6
7
8
9
typedef  unsigned char      boolean;     /* Boolean value type. */   

typedef unsigned long int uint32; /* Unsigned 32 bit value */
typedef unsigned short uint16; /* Unsigned 16 bit value */
typedef unsigned char uint8; /* Unsigned 8 bit value */

typedef signed long int int32; /* Signed 32 bit value */
typedef signed short int16; /* Signed 16 bit value */
typedef signed char int8; /* Signed 8 bit value */

3. 得到指定地址上的一个字节或字

1
2
#define  MEM_B(x)  ( *( (byte *) (x) ) ) 
#define MEM_W(x) ( *( (word *) (x) ) )

4. 求最大值和最小值

1
2
#define  MAX(x, y)  ( ((x) > (y)) ? (x) : (y) ) 
#define MIN(x, y) ( ((x) < (y)) ? (x) : (y) )

5. 得到一个field在结构体(struct)中的偏移量

1
2
#define FPOS( type, field ) / 
/*lint -e545 */ ( (dword) &(( type *) 0)-> field ) /*lint +e545 */

6. 得到一个结构体中field所占用的字节数

1
#define FSIZ( type, field ) sizeof( ((type *) 0)->field ) 

7. 按照LSB格式把两个字节转化为一个Word

1
#define  FLIPW( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] ) 

8. 按照LSB格式把一个Word转化为两个字节

1
2
3
#define  FLOPW( ray, val ) / 
(ray)[0] = ((val) / 256); /
(ray)[1] = ((val) & 0xFF)

9. 得到一个变量的地址(word宽度)

1
2
#define  B_PTR( var )  ( (byte *) (void *) &(var) ) 
#define W_PTR( var ) ( (word *) (void *) &(var) )

10. 得到一个字的高位和低位字节

1
2
#define  WORD_LO(xxx)  ((byte) ((word)(xxx) & 255)) 
#define WORD_HI(xxx) ((byte) ((word)(xxx) >> 8))

11. 返回一个比X大的最接近的8的倍数

1
#define RND8(x)        ((((x) + 7) / 8 ) * 8 ) 

12. 将一个字母转换为大写

1
#define  UPCASE(c)   ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c) ) 

13. 判断字符是不是10进值的数字

1
#define  DECCHK(c)   ((c) >= '0' && (c) <= '9') 

14. 判断字符是不是16进值的数字

1
2
3
#define  HEXCHK(c)   ( ((c) >= '0' && (c) <= '9')  / 
((c) >= 'A' && (c) <= 'F') /
((c) >= 'a' && (c) <= 'f') )

15. 防止溢出的一个方法

1
#define  INC_SAT(val)  (val = ((val)+1 > (val)) ? (val)+1 : (val)) 

16. 返回数组元素的个数

1
#define  ARR_SIZE(a)  (sizeof((a)) / sizeof((a[0]))) 

17. 对于IO空间映射在存储空间的结构,输入输出处理

1
2
3
4
5
6
7
#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)))

18. 使用一些宏跟踪调试

ANSI标准说明了五个预定义的宏名。它们是:

1
2
3
4
5
_LINE_      // 行号
_FILE_ // 文件名
_DATE_ // 日期
_TIME_ // 时间
_STDC_ // 是否符合标准

如果编译器不符合标准,则可能仅支持以上宏名中的几个,或根本不支持。此外,编译器可能会提供其它预定义的宏名。

_LINE__FILE_宏指令在有关#line的部分中已讨论,这里讨论其余的宏名。

_DATE_宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。

源代码翻译到目标代码的时间作为串包含在_TIME_中。串形式为时:分:秒

如果实现是标准的,则宏_STDC_含有十进制常量1。如果它含有任何其它数,则实现是非标准的。

当定义了_DEBUG,输出数据信息和所在文件所在行

1
2
3
4
5
#ifdef _DEBUG 
#define DEBUGMSG(msg,date) printf(msg);printf("%d%d%d",date,_LINE_,_FILE_)
#else
#define DEBUGMSG(msg,date)
#endif

19. 宏中###的用法

1) 一般用法

#把宏参数变为一个字符串,##把两个宏参数连接在一起,用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<cstdio> 
#include<climits>
using namespace std;

#define STR(s) #s
#define CONS(a,b) int(a##e##b)

int main()
{
printf(STR(vck)); // 输出字符串"vck"
printf("%d/n", CONS(2,3)); // 2e3 输出:2000
return 0;
}

2) 当宏参数是另一个宏的时候

需要注意的是凡宏定义里有用###的地方宏参数是不会再展开.

(1) 非###的情况

1
2
#define TOW        (2) 
#define MUL(a,b) (a*b)

如下例子,MUL里的参数TOW会被展开为(2).

1
2
//宏会被展开为: printf("%d*%d=%d/n", (2), (2), ((2)*(2))); 
printf("%d*%d=%d/n", TOW, TOW, MUL(TOW,TOW));

(2) 当有###的时候

假设有如下定义:

1
2
3
4
#define STR(s)     #s 

#define A (2)
#define CONS(a,b) int(a##e##b)

现在希望作如下转换:将INT_MAX的值转换为字符串;

1
2
3
4
5
6
7
8
9
// 下行被展开为: printf("int max: %s/n", "INT_MAX"); 
printf("int max: %s/n", STR(INT_MAX)); // INT_MAX(定义在 #include<climits> 中)
// 注意,此处只进行了一次替换,INT_MAX成为了输出

// 下行被展开为:printf("%s/n", int(AeA));
printf("%s/n", CONS(A, A)); // compile error
// 注意,此处只进行了一次替换,a##e##b变成了 AeA,并没有将A继续替换为2,
// 从而出现编译错误,而不是希望得到的2e2

INT_MAXA都不会再被展开, 解决这个问题的方法很简单,加多一层中间转换宏。使得所有宏的参数全部展开, 从而在转换后的宏里(_STR)获得正确的宏参数.

1
2
3
4
5
6
#define _STR(s)     #s 
#define STR(s) _STR(s) // 转换宏

#define A (2)
#define _CONS(a,b) int(a##e##b)
#define CONS(a,b) _CONS(a,b) // 转换宏

转换后:

1
2
3
4
5
6
7
// 以下语句输出为: int max: 0x7fffffff
// STR(INT_MAX) --> _STR(0x7fffffff) 然后再转换成字符串;
printf("int max: %s/n", STR(INT_MAX)); // INT_MAX,int型的最大值,为一个变量 #include<climits>

// 以下语句输出为:200
// CONS(A, A) --> _CONS((2), (2)) --> int((2)e(2))
printf("%d/n", CONS(A, A));

3) ###的一些应用特例

(1) 合并匿名变量名

1
2
3
#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表示该行行号;

宏的层次 转换前 方向 转换后
第1层: ANONYMOUS(static int); --> __ANONYMOUS0(static int, __LINE__);
第2层: __ANONYMOUS0(static int, __LINE__); --> ___ANONYMOUS1(static int, _anonymous, 70);
第3层: ___ANONYMOUS1(static int, _anonymous, 70); --> static int _anonymous70;

即每次只能解开当前层的宏,所以__LINE__在第二层才能被解开;

(2) 填充结构

1
2
3
4
5
6
7
8
9
#define  FILL(a)   {a, #a} 

enum IDD{OPEN, CLOSE};
typedef struct{
IDD id;
const char * msg;
}MSG;

MSG _msg[] = {FILL(OPEN), FILL(CLOSE)};

相当于:

1
2
MSG _msg[] = {{OPEN, "OPEN"}, 
{CLOSE, "CLOSE"}};

(3) 记录文件名 .

1
2
3
#define  _GET_FILE_NAME(f)   #f 
#define GET_FILE_NAME(f) _GET_FILE_NAME(f)
static char FILE_NAME[] = GET_FILE_NAME(__FILE__);

(4) 得到一个数值类型所对应的字符串缓冲大小

1
2
3
4
5
#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];