LINUX内核中min和max宏的学习

来源:互联网 发布:centos7开放8080端口 编辑:程序博客网 时间:2024/05/29 08:40

add linux kernel min, max define:

include/linux/kernel.h

/*

 * min()/max() macros that also do

 * strict type-checking.. See the

 * "unnecessary" pointer comparison.

 */

#define min(x,y) /

(__extension__/

({ /

        typeof(x) _x = (x);     /

        typeof(y) _y = (y);     /

        (void) (&_x == &_y);            /

        _x < _y ? _x : _y; })     /

)

#define max(x,y) /

(__extension__/

({ /

        typeof(x) _x = (x);     /

        typeof(y) _y = (y);     /

        (void) (&_x == &_y);            /

        _x > _y ? _x : _y; })     /

)

 

 

如上是include/linux/kernel.h中关于min和max两个很常用的宏的定义,在其中我们可以看到以下几个关键点:

1. 为何不用更简单的(x>y)?x:y 这样的方式来定义,而要重新赋值一次。

2. 为何要用({  })这样的结构包围代码块

3. 那个__extension__是什么东东? (有的版本没有这个__extension__)

4. (void)(&_x == &_y)有什么用,不是脱了裤子放屁么

 

 

 

解答:

1. 如果用户使用了min(x++,y++)会有怎样的结果?

   所以最好的办法是避免在宏的实体里让参数出现一次以上。老的linux用的是 x>?y,这个>?操作符是返回x和y中较大的一个,不过GCC的文档里已经决定抛弃这种老的用法了。

 

 

2. GCC的扩展里像{ }这种结构也是可以有值的,其值就是其中最后一个语句的值(包括左值),这里外面再加个括号是为了保险,比如:

   printf("猜猜会显示什么?/n答案是:%d/n",({

                int i = 0;

                int j = 999;

                int x = (i++) + j;

        })

    );

最后的输出会是:

猜猜会显示什么?

答案是:1000

 

 

3. __extension__:

GCC引入了很多标准C中的没有的扩展,如({和)},GCC提供了pednatic选项用于检测程序是否使用了GCC的扩展,当使用pedantic选项编译如下程序时:

int i;

typeof(i) j;

 

这个"typeof"是在GCC的内核里实现的,是GCC的扩展,在其他编译器下无法使用,所以如果编译时增加了-pedantic选项,这里就会报告WARN。

但是呢,现在用__extension__宏将其wrap起来以后,即使使用了-pedantic选项,也不会报告WARN了。

(不过我个人不太认可这种方式,这种方式隐藏了移植中的问题,但是反过来一想,换一个平台编译,那个平台的min和max宏肯定不会使用typeof,所以也许这个写法还是没问题的)

 

 


4.(void)(&_x == &_y)

        (void) (&_x == &_y)这句话本身都执行程序来讲完全是一句废话,它的作用在于,本身我们无法做这样的操作typeof(_x)==typeof(_y),所以故意判断他们2个的地址指针是否相等,显然是不可能相等,但是如果_x和_y的类型不一样,其指针类型也会不一样,2个不一样的指针类型进行比较操作,会抛出一个编译警告。也就是说char *p; int *q; 然后p==q;,这个判断因为一个是char*一个是int*,会在编译时产生一个warning。巧妙就巧妙在这里。

  由于内核是很多开发着一起开发的,其中还有一些其他的实现,就跟我们平常用的一样:

  #define min(a,b) (((a) < (b)) ? (a) : (b))

  试想:

  min(++a,++b) ==> ((++a)<(++b))?(++a):(++b)

  是不是就有问题了,传入的参数被加了两次。