C语言常用宏的使用小结

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

#ifndef __HEAD_H_
#define __HEAD_H_
#endif

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

#define MEM_BYTE(x)  (*((char *)(x)))
#define MEM_HWORD(x)  (*((short *)(x)))
#define MEM_WORD(x)  (*((long *)(x)))

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

#define GET_OFFSET_OF_STRUCT(type,field)	(&(((type *)0)->field))

这里涉及到结构体中成员边界对齐的问题,相同的结构体在不同的CPU和编译器上会得到不同的结果。
如下面的代码,所得到的偏移是不同的。
测试的环境是: MacBookPro OS 10.12.3 + MacOS gcc.

  • sizeof(char)=1,sizeof(short)=2,sizeof(int)=4,sizeof(long)=8,sizeof(float)=4
  • sizeof(struct Student)=40=4+4+8+4+4*5
  • sizeof(struct StudentPacked)=37=1+4+8+4+4*5
struct Student {
	char a; //offset=0
	int b; //4
	long c; //8
	float d; //16
	int e[5]; //20
};

struct StudentPacked {
	char a; //offset=0
	int b; //1
	long c; //5
	float d; //13
	int e[5]; //17
}__attribute__((packed));

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

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

5.返回数组元素的个数

#define ARRAY_SIZE(a)		(sizeof((a)) / sizeof((a[0])))

6.使用__LINE__等宏输出日志

printf("%s %s %d %s %s\n",__FILE__,__FUNCTION__,__LINE__,__DATE__,__TIME__);

输出结果为: …/src/UsefulC.c main 58 Feb 23 2017 01:55:08

  • __LINE____FILE__ 宏:指示程序的当前行数和文件名
  • __DATE__ 宏指令含有形式为月/日/年的字符串,表示源文件被编译的日期。
  • __TIME__ 宏指令包含程序编译的时间。时间用字符串表示,其形式为: 分:秒
  • __STDC__ 宏指令的意义是编译时定义的。一般来讲,如果__STDC__已经定义,编译器将仅接受满足C/C++标准的代码,不包含任何标准的扩展。如果实现是标准的,则宏__STDC__为1。如果它含有任何其它数,则实现是非标准的。
  • __cplusplus 与标准c++一致的编译器把它定义为一个至少为6位的数值。与标准c++不一致的编译器将使用5位或更少的数值
  • 如果编译器不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译器也许还提供其它预定义的宏名

7.宏定义中#,##的用法

#表示把宏定义中的参数转化为字符串, ##表示把宏定义中的参数拼接起来,看实例:

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

printf(STR(vck)); // 输出"vck"
printf("%d\n", CONS(2,3)); // 输出2000,即2e3=2*10^2

注意上面宏的参数是不能加括号的,就是说不能这样定义:

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

深入一点,当宏参数是另一个宏的情况下,宏参数是不会展开的,是不是不知所云,举一个例子,

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

printf(STR(A)); // 输出"A",不是"2"
printf("%d\n", CONS(A,A)); //CONS(A,A)被替换为AeA,而不是2e2

如何避免这个错误呢,很简单,再加一个中间转换宏即可,代码如下,

#define A  2
#define STR(s)	_STR(s)
#define CONS(a,b)	_CONS(a,b)
#define _STR(s)     #s
#define _CONS(a,b)  ((int)(a##e##b))
printf(STR(A));
printf("%d\n", CONS(A,A));

定义_STR这个中间转换宏后,从STR_STR的替换,就实现了A到2的替换,_CONS类似。

这里再举二个实际的应用:

7.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);,如果这条语句定义在源码里的第70行,那么该宏相当于如下语句:
static int _anonymous70;

宏展开过程如下:

  • ANONYMOUS(static int) --> __ANONYMOUS0(static int, LINE)

  • __ANONYMOUS0(static int, LINE) --> ___ANONYMOUS1(static int, _anonymous, 70)

  • ___ANONYMOUS1(static int, _anonymous, 70) --> static int _anonymous70

这里分三个步骤展开,即每次只能展开当前层的宏,所以__LINE__在第二步才能被展开。

7.2 记录文件名

#define _GET_FILE_NAME(f)   #f
#define GET_FILE_NAME(f)    _GET_FILE_NAME(f)

static char FILE_NAME[] = GET_FILE_NAME(__FILE__);

8 利用#error在预处理阶段做提示

#ifdef __cplusplus
#error "you use c++ compiler now, please use c compiler"
#endif

字符串两边的双引号可不加,但是一般会加上,提高可读性。

9 得到一个变量的地址

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

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

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

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

#define ROUND8(x) ((((x) + 7)/8) * 8)

12 将一个字母转换为大写

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

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

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

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

#define HEX_CHK(c) (((c) >= '0' && (c)<='9') || ((c)>='A' && (c)<= 'F') || \
((c)>='a' && (c)<='f'))

15 求最大值

#define MAX2(a,b)  ((a) > (b) ? (a) : (b))
#define MAX3(a,b,c)  ((a) > (b) ? ((a) > (c) ? (a) : (c)) : ((b) > (c) ? (b) : (c)))

16 位操作

#define SET(n,i) ((n) | (1u<<i)) //置n的bit i为1

#define CLEAR(n,i) ((n) & (~(1u<<i))) //清n的bit i

#define TOGGLE(n,i) ((n) ^ (1u<<i)) //bit i取反

#define TEST(n,i) !!((n)&(1u<<i)) //测试bit i是否为1

17 循环移位

//循环左移n位,假设val为无符号数
#define ROTL(val,n) (((val)<<n) | ((val)>>(sizeof(val)*8-n)))

//循环右移n位,假设val为无符号数
#define ROTR(val,n) (((val)>>n) | ((val)<<(sizeof(val)*8-n)))

18 宏实现SWAP交换

#define SWAP(x,y) ((x)==(y) ? NULL:((x)^=(y),(y)^=(x),(x)^=(y))) 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值