C语言宏定义-max、min宏再看Linux内核问题演进

C语言宏定义-max、min宏再看Linux内核问题演进

1. 简介

1) 通常我们经常使用如下宏判断返回X、Y的最小值或者最大值

#define min(X, Y)  ((X) < (Y) ? (X) : (Y))
#define max(X, Y)  ((X) < (Y) ? (Y) : (X))
  • 宏参数可能被重复调用的问题:例如 next=min(++a,1),宏被展开以后等于next=((++a) < (1)) ? (++a) : (1)),看出来宏的参数++a被调用了2次,又如:next = min (x + y, foo (z));,宏体被展开成next = ((x + y) < (foo (z)) ? (x + y) : (foo (z))),foo(z)也被调用了两次,可能程序会产生逻辑错误,不是我们期待的值。

2) 再来看看Linux 内核定义怎么解决这个问题的

#define min(a, b) ({ \
    typeof(a) __min1__ = (a);  \
    typeof(b) __min2__ = (b);  \
    (void)(&__min1__ == &__min2__);  \
    __min1__ < __min2__ ? __min1__ : __min2__;})
  • 用到了GCC的一个扩展特性({statement list}),形如({ ... })这样的代码块会被视为一条语句,其计算结果是{ ... }中最后一条语句的计算结果

  • 先根据a, b的类型生成了两个局部变量__min1____min2__,之后比较其大小,返回较小的一个,这样就保证了宏参数只会被执行一次,避免了上述参数两次被执行的副作用

  • (void)(&__min1__ == &__min2__) 判断参数a、b类型是否相同,不相同编译器将产生警告,相当美妙

3) 看到这里认为一切就好了吗,继续看下去,膜拜一下Linux内核开发者的严谨性

我们先考虑一种情况,假如外部使用参数名称和min宏定义实现中的局部变量__min1__ __min2__重名了,会发生什么情况,例如

int __min1__ = 10;
int __min2__ = 20;
int min_val = min(__min1__--, __min2__++);

我们期待的结果:__min1__ = 9, __min2__ = 21, min_val = 10,但是最后情况是__min1__ = 10, __min2__ = 20min_val未知。造成这一结果的原因在于输入参数和宏定义内部使用的局部变量重名了,这样就会导致在宏定义的语句块内,外层同名变量的作用域被内层局部变量的作用域所屏蔽,展开后的代码就成了这样:

int main_val = ({typeof(__min1__--) __min1__ = (__min1__--); /* 省略 */ })

这就类似于int a = a这样的语句,执行完后a的值是不确定的,且因为展开后__min1__成了宏体内的局部变量,__min1__--的自减操作对于外层变量来说也是无效的。

其实最初宏定义中使用__min1__这样的名字也是为了避免重名,但是也可能存在重名的风险,内核引入了__UNIQUE_ID可以生成一个唯一的名字。唯一性是由编译器提供的__COUNTER__宏来保证,当然我们编写代码不应该再使用类似__UNIQUE_ID_min1_0变量名称,这样刻意造成重名

/* Indirect macros required for expanded argument pasting, eg. __LINE__. */
#define ___PASTE(a,b) a##b
#define __PASTE(a,b) ___PASTE(a,b)

#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)

/*
 * min()/max()/clamp() macros that also do
 * strict type-checking.. See the
 * "unnecessary" pointer comparison.
 */
#define __min(t1, t2, min1, min2, x, y) ({      \
    t1 min1 = (x);                  \
    t2 min2 = (y);                  \
    (void) (&min1 == &min2);            \
    min1 < min2 ? min1 : min2; })
#define min(x, y)                   \
    __min(typeof(x), typeof(y),         \
          __UNIQUE_ID(min1_), __UNIQUE_ID(min2_),   \
          x, y)

4)max宏,在后面内核有进行过一轮整改和演进,具体查看下面文章分析:

内核 max宏分析

Linus都要再三修正的max()宏是怎样演变的

2. __COUNTER__

__COUNTER__ 宏是GCC GNU的一种扩展,会被展开为一个从0开始的整数,且每次调用后其值都会加一,这也就保证了其唯一性。

根据 GCC.GNU 的文档

image-20201116182007272

3. 相关宏

CMO_MIN(x, y)返回x、y中最小值
CMO_MAX(x, y)返回x、y中最大值
CMO_ABS(x)返回x的绝对值
CMO_CLAMP(x, tmin, tmax)用来将某个值“钳”住、返回值限制在[tmin, tmax]这个区间范围内
CMO_INRANAGE(x, tmin, tmax)如果x在[tmin, tmax]内返回true,否则返回false
/**
 * @def CMO_MIN
 * @brief Return smaller value of two provided expressions.
 */
/**
 * @def CMO_MAX
 * @brief Return larger value of two provided expressions.
 */
#if (CMO__HAVE_TYPEOF == 1)
#define CMO_MIN(x, y)                                                        \
    ({                                                                       \
        typeof(x) ____min_x_ = (x);                                          \
        typeof(y) ____min_y_ = (y);                                          \
        (void)(&____min_x_ == &____min_y_);                                  \
        ____min_x_ < ____min_y_ ? ____min_x_ : ____min_y_;                   \
    })
#define CMO_MAX(x, y)                                                        \
    ({                                                                       \
        typeof(x) ____max_x_ = (x);                                          \
        typeof(y) ____max_y_ = (y);                                          \
        (void)(&____max_x_ == &____max_y_);                                  \
        ____max_x_ > ____max_y_ ? ____max_x_ : ____max_y_;                   \
    })
#else
#define CMO_MIN(x, y) ((x) < (y) ? (x) : (y))
#define CMO_MAX(x, y) ((x) < (y) ? (y) : (x))
#endif /* (CMO__HAVE_TYPEOF == 1) */

/**
 * @def CMO_ABS
 * @brief Return absolute value of the provided expressions.
 */
#define CMO_ABS(x) ((x) > 0 ? (x) : -(x))

/**
 * @def CMO_CLAMP
 * @brief Clamp one number between a min and max.
 *
 * ```c
 * if (x < tmin)      return tmin;
 * else if (x > tmax) return tmax;
 * else               return x;
 * ```
 */
#define CMO_CLAMP(x, tmin, tmax) (CMO_MAX(CMO_MIN((x), (tmax)), (tmin)))

/**
 * @def CMO_INRANAGE
 * @brief Return true, when one number between a min and max, otherwise false.
 */
#define CMO_INRANAGE(x, tmin, tmax) ((x) >= (tmin) && (x) <= (tmax))
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值