1.宏的各种符号
1.1关于宏参数里#的知识
在了解这个小知识点之前,我们先来看看下面这道题
#define FUN(a) "a"
FUN(345)
思考一下现在a会不会被替换成345?
答案是否定的,""内的字符不能当做形参。那么我们如何才能让它能够替换a?
#define FUN(a) #a
. #是字符串化的意思,将a转成一个字符串
1.2.关于宏参数里##的知识
把宏参数与宏定义的代码段的标识符连接在一起
#define Conn(x,y) x##y
int n = Conn(123,456); /* 结果就是n=123456;*/
1.3.关于宏参数里#@的知识
它将单字符标记符变换为单字符,结果返回一个const char类型
#define ToChar(x) #@x
char a = ToChar(1);结果就是a='1';
2.宏替换发生的时间
源程序在编译的时候经历了预处理、编译、汇编、连接几个过程,而预处理会实现以下几个功能
-
文件包含
这时候会将#include的文件找到并展开到该处。 -
条件编译
预处理器根据#ifdef #if等将源程序的某部分包含进来或排除在外。 -
宏展开
将源程序中有使用到宏的地方展开成相应的宏定义,在这个阶段所进行的工作只是纯粹的替换与展开,没有任何计算功能
3.宏的各种奇妙用法
3.1 得到一个field在结构体(struct)中的偏移量
#define OFFSETOF( type, field ) ( (size_t) &(( type *) 0)-> field )
上面的这个宏定义是如何实现的呢?
我们可以将这个宏分为五个步骤
- 0
- (type *)0
- ((type *)0)->field
- &((type *)0)->field
- (size_t) &(( type *) 0)-> field
- 内存地址从0开始
- 将0转为type的结构体指针,也就是说编译器认为这个结构体是开始于程序段起始位置
- 引用结构体的field成员
- 取该成员的地址
- 强转换为size_t
3.2 得到一个结构体中field所占用的字节数
#define FSIZ( type, field ) sizeof( ((type *) 0)->field )
3.3 防止溢出的一个方法
#define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))
3.4 防止一个头文件被重复包含
#ifndef BODYDEF_H
#define BODYDEF_H
//头文件内容
#endif
网友评论