实例1:

宏定义计算问题

#define PRODUCT (x) (x*x)

int main()
{
 	int a,b = 3;
 	a = PRODUCT(b+2);
}

求a的值:

结果为:a = b+2*b+2=3+2*3+2=11

实例2:

描述下面XXX这个宏的作用。

#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
#define XXX(ptr,type,member)({\
	const typeof(((type*)0)->member) *__mpter=(ptr);\
	(type*)((type*)__mpter - offsetof(type,member));})

第一个宏:

#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE*)0)->MEMBER)

  • (TYPE*)0,将0强制转换为TYPE类型的指针,并且指向了0地址空间。
  • (TYPE*)0)->MEMBER,指向结构体中的成员
  • &((TYPE*)0)->MEMBER,获取成员在结构体中的位置,因为其实位置为0,所以获取的地址即为实际的偏移地址。
  • (size_t)&((TYPE*)0)->MEMBER,将偏移地址强制转换为size_t,即unsigned int。
(TYPE*)0				=>	p = (TYPE*)0
(TYPE*)0)->MEMBER		=>	p->MEMBER
&((TYPE*)0)->MEMBER)	=>	&(p->MEMBER)	//成员MEMBER的地址

第二个宏:

#define XXX(ptr,type,member)({\
	const typeof(((type*)0)->member) *__mpter=(ptr);\
	(type*)((type*)__mpter - offsetof(type,member));})
  • ({ })的语法形式是GNU C编译器的语法扩展,与逗号表达式类似,表达式结果为最后一个语句的值。
  • typeof主要用于宏定义之中,可以使用typeof关键字来引用宏参数的类型。
  • typeof(((type*)0)->member),引用type结构体的member成员的数据类型。
  • const typeof(((type*)0)->member) *__mpter=(ptr);,定义一个与type结构体的member成员相同的类型的指针变量__mpter,而且将ptr赋值给它。
  • offsetof(type,member),获取member成员在type结构中的偏移量。
  • (type*)((type*)__mpter - offsetof(type,member));,将__mpter减去偏移量,就得到了这个结构变量的地址了(指针)。

结构变量的地址

ptr是指向正被使用的某类型变量指针;

type是包含ptr指向的变量类型的结构类型;

member是type结构体中的成员,类型与ptr指向的变量类型一样;

宏定义的功能是:计算返回包含ptr指向的变量所在的type类型结构变量的指针。

参考:老生常谈的Linux内核中常用的两个宏定义

实例3:

用C语言字节编写一下四个宏

ALGN_DOWN(x, a)将数值x按照a的整数倍向下取整,例如ALGN_DOWN(65,3) = 63;

ALGN_UP(x, a)将数值x按照a的整数倍向上取整,例如ALGN_UP(65,3) = 66;

ALGN_2N_DOWN(x, a)将数值x按照a的整数倍向下取整,a是2的n次幂,例如ALGN_2N_DOWN(65,4) = 64;

ALGN_2N_UP(x, a)将数值x按照a的整数倍向上取整,a是2的n次幂,例如ALGN_2N_UP(65,4) = 68。

设条件为:

int x; 
int a = 8;

将数值x按照a的整数倍向下取整,a是2的n次幂:

对齐掩码a_mask = 11111 11111 11111 11111 11111 11110 00,即 ~(a- 1)

对齐掩码a_mask与a进行”与运算”就可以得到,将数值x按照a的整数倍向下取整,a是2的n次幂。

#ALGN_2N_DOWN(x, a) (x & ([~(a-1)]) )

将数值x按照a的整数倍向上取整,a是2的n次幂

如果要求出比x大的是不是需要加上a就可以了?可是如果x本身就是a的倍数,这样加a不就错了吗?

所以在x基础上加上(a - 1),然后与a的对齐掩码进行与运算。

#ALGN_2N_UP(x, a) ((x+a-1) & (~ (a-1)))

实例:

x=0, a=8, 则ALGN_2N_DOWN(x,a)=0, ALGN_2N_UP(x,a)=0.
x=6, a=8, 则ALGN_2N_DOWN(x,a)=0, ALGN_2N_UP(x,a)=8.
x=8, a=8, 则ALGN_2N_DOWN(x,a)=8, ALGN_2N_UP(x,a)=8.
x=14, a=8,则ALGN_2N_DOWN(x,a)=8, ALGN_2N_UP(x,a)=16.
注:a应当为2的n次方, 即2, 4, 8, 16, 32, 64, 128, 256, 1024, 2048, 4096 ...

参考:内核宏ALIGN的含义

实例4

用预处理指令#define声明一个常数,用以表示1年中有多少秒(忽略闰年问题)

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

一年的秒数为31536000,16位的int型将会溢出,因此要使用长整型符号L

实例5

写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。

#define MIN(A,B) ((A)<=(B)?(A):(B))

容易错的地方:宏中间要小心地把参数用括号括起来。