container_of()和内核链表中的list_entry()

本文详细解析了C语言中的container_of宏与list_entry宏。container_of宏用于从结构体成员指针中获取结构体起始地址,而list_entry宏则用于从链表节点指针中获取所属结构体的地址。文章通过具体示例展示了这两个宏的使用方法。

(1) container_of宏的原始定义是: 

  1. /**
  2. * container_of - cast a member of a structure out to the containing structure
  3. * @ptr:    the pointer to the member.
  4. * @type:   the type of the container struct this is embedded in.
  5. * @member: the name of the member within the struct.
  6. * */ 
  7.  
  8. #define container_of(ptr, type, member) ({                    /         
  9.         const typeof( ((type *)0)->member ) *__mptr = (ptr); -/ 
  10.         (type *)( (char *)__mptr - offsetof(type,member) );}) 
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr:    the pointer to the member.
* @type:   the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
* */

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

       该宏的作用是:指针ptr是指向结构体type中的成员member;通过指针ptr,该宏返回结构体type的起始地址。模型如下:

          type
      |----------|
      |          |
      |          |
      |----------|
ptr-->| member --|
      |----------|
      |          |
      |          |
      |----------|

        分析可知__mptr指向的是一个type结构里typeof(((type *)0)->member)类型member成员的指针,offsetof(type,member)是这个成员member在结构type中的偏移,单位是字节,所以为了计算type结构的起始地址,__mptr减去它自己的偏移。

说明:TYPE是某struct的类型,0是一个假想TYPE类型struct地址,MEMBER是该struct中的一个成员;由于该struct的基地址为0, MEMBER的地址就是该成员相对与struct头地址的偏移量。const typeof( ((type *)0->member ) *__mptr = (ptr);意思是声明一个与member同一个类型的指针常量 *__mptr,并初始化为ptr。(type *)( (char *)__mptr - offsetof(type,member) );意思是__mptr的地址减去member在该struct中的偏移量得到的地址, 再转换成type型指针,该指针就是member所在结构体的入口地址了。

例一,通过一个程序来验证

  1. include<stdio.h> 
  2. struct student{ 
  3.     char name[20];   
  4.     char sex; 
  5. }stu={"zhangsan",'m'}; 
  6. main() 
  7.       struct student *stu_ptr;    //存储container_of宏的返回值 
  8.       int offset;                          //存储offsetof宏的返回值 
  9.       const typeof(((struct student*)0)->sex) *_mptr = &stu.sex; 
  10.       offset = (int)(&((struct student *)0)->sex); 
  11.       stu_ptr = (struct student *)((char*)_mptr - offset); 
  12.       printf("offsetof stu.sex = %d/n",offset);  //输出偏移 
  13.       printf("stu_ptr->name:%s/tstu_ptr->sex:%c/n", stu_ptr->name, stu_ptr->sex); 
  14.       return 0; 
include<stdio.h>
struct student{
    char name[20];  
    char sex;
}stu={"zhangsan",'m'};
main()
{
      struct student *stu_ptr;    //存储container_of宏的返回值
      int offset;                          //存储offsetof宏的返回值
      const typeof(((struct student*)0)->sex) *_mptr = &stu.sex;
      offset = (int)(&((struct student *)0)->sex);
      stu_ptr = (struct student *)((char*)_mptr - offset);
      printf("offsetof stu.sex = %d/n",offset);  //输出偏移
      printf("stu_ptr->name:%s/tstu_ptr->sex:%c/n", stu_ptr->name, stu_ptr->sex);
      return 0;
}

        综上那就是根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针。至于offsetof的用法,我们同样可以采用一段程序来说明:

  1. struct student  
  2.      char name[20]; 
  3.      char sex; 
  4. }; 
  5. main() 
  6.       printf("value is %d /n",(unsigned int)&((struct student *)0)->sex); 
struct student 
{
     char name[20];
     char sex;
};
main()
{
      printf("value is %d /n",(unsigned int)&((struct student *)0)->sex);
}

        结果得20,恰为偏移值。

(2)list_entry宏的定义

  1. /**
  2. * list_entry - get the struct for this entry
  3. * @ptr:    the &struct list_head pointer.
  4. * @type:   the type of the struct this is embedded in.
  5. * @member: the name of the list_struct within the struct.
  6. */ 
  7. #define list_entry(ptr, type, member) \ 
  8.     container_of(ptr, type, member) 
/**
 * list_entry - get the struct for this entry
 * @ptr:	the &struct list_head pointer.
 * @type:	the type of the struct this is embedded in.
 * @member:	the name of the list_struct within the struct.
 */
#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)

       该宏的作用是根据链表的一个成员可以访问链表结构的其他成员。比如如下的一个结构体:

struct demo{

      long data;

      struct list_head list;

}

在知道成员变量list地址(假设Ptr)的情况下,通过list_entry(Ptr,struct demo,list)->data即可访问成员变量data。由此延伸出来的内核遍历函数list_for_each_entry就是基于这个宏实现的:

  1. /**
  2. * list_for_each_entry  -   iterate over list of given type
  3. * @pos:    the type * to use as a loop cursor.
  4. * @head:   the head for your list.
  5. * @member: the name of the list_struct within the struct.
  6. */ 
  7. #define list_for_each_entry(pos, head, member)              \ 
  8.     for (pos = list_entry((head)->next, typeof(*pos), member);   \ 
  9.          prefetch(pos->member.next), &pos->member != (head);  \ 
  10.          pos = list_entry(pos->member.next, typeof(*pos), member)) 
/**
 * list_for_each_entry	-	iterate over list of given type
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 */
#define list_for_each_entry(pos, head, member)				\
	for (pos = list_entry((head)->next, typeof(*pos), member);	\
	     prefetch(pos->member.next), &pos->member != (head); 	\
	     pos = list_entry(pos->member.next, typeof(*pos), member))

       其中,head是个链表头,它是pos结构体中的成员,member就是struct list_head的list。如:

struct i2c_devinfo {
       struct list_head list;
       int   busnum;
       struct i2c_board_info board_info;
};

struct list_head __i2c_board_list;

struct i2c_devinfo *devinfo;

list_for_each_entry(devinfo, &__i2c_board_list, list)


原文地址:http://blog.youkuaiyun.com/yinkaizhong/archive/2009/04/20/4093795.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值