目录
1. 用一个宏实现求两个数中的最大(小)值
1.1 最常见的实现方法
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
上述实现方式对于 参数具有副作用 的情况,很容易出现意想不到的结果。比如:
int a = 2;
int b = 3;
int max = MAX(a++, b++);
int min = MIN(a++, b++);
1.2 防止参数副作用
优化:在比较之前,保留一份备份,用备份的参数来进行对比。
#define MAX(X, Y) ({ \
int _X = (X); \
int _Y = (Y); \
_X > _Y ? _X : _Y; \
})
#define MIN(X, Y) ({ \
int _X = (X); \
int _Y = (Y); \
_X < _Y ? _X : _Y; \
})
上述实现方式仅适用于两个 int 类型数据比较。
1.3 指定参数类型
优化:传入 type 参数。
#define MAX(TYPE, X, Y) ({ \
TYPE _X = (X); \
TYPE _Y = (Y); \
_X > _Y ? _X : _Y; \
})
#define MIN(TYPE, X, Y) ({ \
TYPE _X = (X); \
TYPE _Y = (Y); \
_X < _Y ? _X : _Y; \
})
上述实现方式有可能传入两个不同类型的数据,因此需要提前判断类型是否相同。
1.4 typeof 获取参数类型
#define MAX(X, Y) ({ \
typeof(X) _X = (X); \
typeof(Y) _Y = (Y); \
(void)(&_X == &_Y); \
_X > _Y ? _X : _Y; \
})
#define MIN(X, Y) ({ \
typeof(X) _X = (X); \
typeof(Y) _Y = (Y); \
(void)(&_X == &_Y); \
_X < _Y ? _X : _Y; \
})
- 通过 typeof 关键字,来获取参数的类型,并且保存参数的一份拷贝。
- 通过 &_X ==&_Y 的比较结果来判断类型是否一致,实际上就是判断 _X 和 _Y 的指针是否相同。
- 而 &_X ==&_Y 的比较结果我们并没有使用,gcc 编译时就会有警告(-Wunused-value),强制转换为 void 可避免该警告。
实际上,最后这段代码是 Linux 内核中关于求两个数中最大(小)值的宏定义,有兴趣的话可以看下头文件:
linux/tools/perf/util/include/linux/kernel.h
2. 交换数字
#define SWAP(X, Y) do { \
(void) (&X == &Y); \
X = X ^ Y; \
Y = X ^ Y; \
X = X ^ Y; \
} while (0)
#define SWAP(X, Y) do { \
typeof(X) _TMP = (X); \
(X) = (Y); \
(Y) = _TMP; \
} while (0)
3. 出错处理
#include <stdio.h>
#include <stdlib.h>
#define SYS_ERR(MSG) do { \
perror(MSG); \
exit(EXIT_FAILURE); \
} while(0)
#define ERROR_CHECK(x) do { \
if (-1 == (x)) { \
SYS_ERR(#x); \
} \
} while (0)
#define ERROR_CHECK_MSG(x, MSG) do { \
if (-1 == (x)) { \
SYS_ERR(MSG); \
} \
} while (0)
#define HANDLER_ERR(MSG, EN) do { \
errno = EN; \
fprintf(stderr, "%s: %s\n", \
MSG, strerror(errno)); \
exit(EXIT_FAILURE); \
} while(0)
4. 计算结构体中某个变量相对于结构体起始位置的偏移量
/*
* 1. ((TYPE *)0) 就是将 0 转换为 TYPE 类型的结构体指针
* 2. (((TYPE *)0)->MEMBER) 就是引用结构体中 MEMBER 成员(或者说通过结构体指针指向 MEMBER 成员)
* 3. &(((TYPE *)0)->MEMBER) 就是对 MEMBER 取地址
* 4. ( (size_t) &(((TYPE *)0)->MEMBER) ) 就是 MEMBER 相对于 0 地址(结构体起始位置)的偏移量
*/
#define OFFSETOF(TYPE, MEMBER) ( (size_t) &(((TYPE *)0)->MEMBER) )
5. 通过成员变量的地址获取它所在结构体的首地址
/*
* 1. typeof() 用来返回一个变量的类型
* 2. ((TYPE *)0) 就是将 0 转换为 TYPE 类型的结构体指针
* 3. (((TYPE *)0)->MEMBER) 就是引用结构体中 MEMBER 成员(或者说通过结构体指针指向 MEMBER 成员)
* 4. const typeof(((TYPE *)0)->MEMBER) *__MPTR = (PTR);
* 首先通过 typeof() 取出 MEMBER 的类型, 然后定义一个中间指针(避免对 PTR 和 PTR 指向的内容造成破坏)保存地址
* 5. OFFSETOF(TYPE, MEMBER) 计算出 MEMBER 相对于结构体起始位置的偏移量
* 6. (TYPE *)( (char *)__MPTR - OFFSETOF(TYPE, MEMBER) );
* (char *)__MPTR 将 __MPTR 转换为 char *, 是因为 OFFSETOF 得到的偏移量是以 字节为单位
* ( (char *)__MPTR - OFFSETOF(TYPE, MEMBER) ) __MPTR 得到的是结构体成员 MEMBER 的指针, 相较于结构体的起始地址是比较大的
* __MPTR 的地址 减去 MEMBER 的偏移量, 得到的就是 结构体的起始位置.(结构体起始位置 + MEMBER 偏移量 = MEMBER 的地址)
* (TYPE *)( (char *)__MPTR - OFFSETOF(TYPE, MEMBER) ); 最后强转为 TYPE 类型的指针
*/
#define CONTAINER_OF(PTR, TYPE, MEMBER) ({ \
const typeof(((TYPE *)0)->MEMBER) *__MPTR = (PTR); \
(TYPE *) ((char *)__MPTR - OFFSETOF(TYPE, MEMBER)); \
})