【C】常用的宏实现

目录

1. 用一个宏实现求两个数中的最大(小)值

1.1 最常见的实现方法

1.2 防止参数副作用

1.3 指定参数类型

1.4 typeof 获取参数类型

2. 交换数字

3. 出错处理

4. 计算结构体中某个变量相对于结构体起始位置的偏移量

5. 通过成员变量的地址获取它所在结构体的首地址


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)); \
})

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值