内核链表中list_entry()函数 以及typeof的用法详解

一、内核链表中list_entry()函数的解读

在看内核链表这部分的时候看到list_entry,查看其代码发现就是前面

#define list_entry(ptr, type, member) \ container_of(ptr, type, member)

所以在这分析一下container_of宏和其中的offsetof宏

1、#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER )

       宏功能:获得一个结构体变量成员在此结构体中的偏移量。

       1. ( (TYPE *)0 ) 将零转型为TYPE类型指针;

       2. ((TYPE *)0)->MEMBER 访问结构中的数据成员;

       3. &( ( (TYPE *)0 )->MEMBER )取出数据成员的地址,即相对于0的偏移量,要的就这个;

       4.(size_t)(&(((TYPE*)0)->MEMBER))结果转换类型,size_t应该最终为unsigned int类型。

       此宏的巧妙之处在于将 0 转换成(TYPE*),这样结构体中成员的地址即为在此结构体中的偏移量。

示例:

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #define offsetof(TYPE, MEMBER) ((int)(&((TYPE *)0)->MEMBER))  
  3. struct _test_{  
  4.     int  x;  
  5.     int  y;  
  6.     float z;  
  7. };  
  8.   
  9. int main(void)  
  10. {  
  11.  int temp = -1;  
  12.  temp = offsetof(struct _test_, z);  
  13.  printf("temp = %d\n", temp);  
  14.  return 0;  
  15. }  

 运行后结构为:temp = 8。

显然求出了 结构体成员变量 z 在结构体中的偏移量为 8。


2、#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

     宏功能:从结构体(type)某成员变量(member)指针(ptr)来求出该结构体(type)的首指针。

     这里比较啰嗦,举个例子吧,然后分析:

[cpp]  view plain copy
  1. #include <stdio.h>  
  2.   
  3. #define offsetof(TYPE, MEMBER) ((int)(&((TYPE *)0)->MEMBER))  
  4.   
  5. #define container_of(ptr, type, member) ({   \  
  6.         const typeof( ((type *)0)->member ) *__mptr = (ptr); \  
  7.         (type *)( (char *)__mptr - offsetof(type,member) );})  
  8.   
  9. struct _test_  
  10. {  
  11.  int  x;  
  12.  int  y;  
  13.  int  z;  
  14. };  
  15.   
  16. void Assignment(struct _test_ *t)  
  17. {  
  18.  t->x = 1;  
  19.  t->y = 2;  
  20.  t->z = 3;  
  21. }  
  22.   
  23. void GetheadPoint(int *tz)  
  24. {  
  25.  struct _test_ *p;  
  26.  int temp = -1;  
  27.   
  28.  p = container_of(tz,struct _test_, z);   //根据成员变量的地址获得该结构体的首地址  
  29.  temp = p->y;                             //根据首地址获得其中另外一个成员变量的值  
  30.   
  31.  printf("line31 = %d\n", temp);  
  32. }  
  33.   
  34. int main(void)  
  35. {  
  36.  int temp = -1;  
  37.   
  38.  struct _test_ tmp;                       //定义一个结构体变量  
  39.   
  40.  Assignment(&tmp);                        //给这个变量赋值  
  41.  GetheadPoint(&tmp.z);                    //只传给这个函数一个结构体成员变量的地址  
  42.   
  43.  printf("line43 tmp - >x = %d\n", tmp.x);  
  44.    
  45.  return 0;  
  46. }  

运行结果为:

line31 = 2
line43 tmp - >x = 1

大概解释一下:先定义一个结构体并给其赋值,现在 GetheadPoint函数只传入这个结构体的一个子成员变量地址。在 GetheadPoint函数中通过container_of宏来获得此结构体的首指针,通过此首指针即可以获得此结构体的其它子成员的值。

       1、typeof( ( (type *)0)->member )为取出member成员的变量类型(请参考后面对typeof的用法说明)。

       2、定义__mptr指针ptr为指向该成员变量的指针

       3、mptr为member数据类型的常量指针,其指向ptr所指向的变量处

       4、.(char *)__mptr转换为字节型指针。(char *)__mptr - offsetof(type,member))用来求出结构体起始地址(为char *型指针),然后(type *)( (char *)__mptr -offsetof(type,member) )在(type *)作用下进行将字节型的结构体起始指针转换为type *型的结构体起始指针。
       5、.({ })这个扩展返回程序块中最后一个表达式的值。

二、typeof关键字  

container_of(ptr, type, member)中包含typeof关键字,我们在这分析一下typeof

typeof关键字是C语言中的一个新扩展。只要可以接受typedef名称,Sun Studio C 编译器就可以接受带有typeof的结构,包括以下语法类别:

  • 声明
  • 函数声明符中的参数类型链表和返回类型
  • 类型定义
  • 类型操作符s
  • sizeof操作符
  • 复合文字
  • typeof实参

编译器接受带双下划线的关键字:__typeof__typeof__。本文中的例子并没有遵循使用双下划线的惯例。从语句构成上看,typeof关键字后带圆括号,其中包含类型或表达式的名称。这类似于sizeof关键字接受的操作数(与sizeof不同的是,位字段允许作为typeof实参,并被解释为相应的整数类型)。从语义上看,typeof 关键字将用做类型名(typedef名称)并指定类型。

1、使用typeof的声明示例

下面是两个等效声明,用于声明int类型的变量a

typeof(int) a; /* Specifies variable a which is of the type int */ 
typeof('b') a; /* The same. typeof argument is an expression consisting of 
                    character constant which has the type int */

以下示例用于声明指针和数组。为了进行对比,还给出了不带typeof的等效声明。

typeof(int *) p1, p2; /* Declares two int pointers p1, p2 */
int *p1, *p2;

typeof(int) * p3, p4;/* Declares int pointer p3 and int p4 */
int * p3, p4;

typeof(int [10]) a1, a2;/* Declares two arrays of integers */
int a1[10], a2[10];

如果将typeof用于表达式,则该表达式不会执行。只会得到该表达式的类型。以下示例声明了int类型的var变量,因为表达式foo()int类型的。由于表达式不会被执行,所以不会调用foo函数。

extern int foo();
typeof(foo()) var;

2、使用typeof的声明限制

请注意,typeof构造中的类型名不能包含存储类说明符,如externstatic。不过允许包含类型限定符,如constvolatile。例如,下列代码是无效的,因为它在typeof构造中声明了extern

typeof(extern int) a;

下列代码使用外部链接来声明标识符b是有效的,表示一个int类型的对象。下一个声明也是有效的,它声明了一个使用const限定符的char类型指针,表示指针p不能被修改。

extern typeof(int) b;
typeof(char * const) p = "a";

3、在宏声明中使用typeof

typeof构造的主要应用是用在宏定义中。可以使用typeof关键字来引用宏参数的类型。因此,在没有将类型名明确指定为宏实参的情况下,构造带有所需类型的对象是可能的。

你可能感兴趣的:(内核链表中list_entry()函数 以及typeof的用法详解)