您的位置:首页 > 大数据 > 人工智能

container_of,typeof详解

2015-09-01 10:26 393 查看
1.源码

/**

 * container_of - 通过结构体的一个成员获取容器结构体的指针

 * @ptr: 指向成员的指针。

 * @type: 成员所嵌入的容器结构体类型。

 * @member: 结构体中的成员名。

 *

 */

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


    const typeof( ((type *)0)->member ) *__mptr = (ptr); \

   (type *)( (char *)__mptr - offsetof(type,member) );})

 这个宏的作用,就是通过一个容器(结构体)中某个成员的指针得到指向这个容器(结构体)的指针,简单的说就是通过成员
 找容器。

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

2.用法

例如:

typedef struct frame {

        int a;

        char b;

        char name[10];

} frame_t;

int main(int argc, char **argv)

{

        frame_t fra, *pf;

        fra.a = 1;

        fra.b = 2;

        snprintf(fra.name, 5, "cjz%d", 1);

        pf = container_of(&fra.a, frame_t, a);

        printf("fra.a = %d, fra.b = %d, fra.name = %s\n", fra.a, fra.b, fra.name);

        return 0;

}

参考:http://blog.csdn.net/cuijianzhongswust/article/details/8249352

3.详解

先分析一下这个 宏的运行机理:

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

一共4步 

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

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

3. &( ( (TYPE *)0 )->MEMBER )取出数据成员的地址; 

4.(size_t)(&(((TYPE*)0)->MEMBER))结果转换类型。巧妙之处在于将0转 换成(TYPE*),结构以内存空间首地址0作为起始地址,则成员地址自然为偏移地址;

注释:

1.要将地址赋给指针, int *p = 0x12345678是不对的 。正确的方式应为:int *p = (int *) 0x12345678。在大多数计算机中,内存地址确实是以无符号整型数来表示的,而且多以16进制表示,但我们在C语言中不能用整型数去表示地址,只能用指针常量来表示,因为它是被用来赋给一个指针的。

对于这个赋值问题还可以换一个角度去理解,在C语言中,使用赋值操作符时,赋值操作符左边和右边的表达式类型应该是相同的,如果不是,赋值操作符将试图把右边表达式的值转换为左边的类型。所以如果写出int *p = 0x12345678 ; 这条语句编译器会报错:'=' : cannot convert from ' const int ' to ' int * ' ,因为赋值操作符左边和右边的表达式的类型应该相同,而0x12345678是int型常量,p是一个指向int型的指针,两者类型不同,所以正确的方式是:int
*p = (int *) 0x12345678 ; 

故((TYPE*)0)是将地址为0转换成TYPE*型指针。即地址0处存放指向TYPE类型数据的地址。则((TYPE*)0)->MEMBER指向TYPE类型数据的成员MEMBER.((TYPE*)0)->MEMBER相当于(*((TYPE*)0)).MEMBER.

 

参考:http://blog.chinaunix.net/uid-28458801-id-4200573.html

(1)const typeof( ((type *)0)->member ) *__mptr = (ptr);                                          typeof即将参数*_mptr定义为((type *)0)->member类型,即复制ptr。

定义一个中间变量__mptr,它等于提供给宏的参数ptr,也就是指向某个成员的指针。这个中间变量的命名意义是:

"__"代表内部使用,内核编程中常常这么做;
“m”代表middle。

    为了避免对 ptr及prt指向的内容造成破坏,这里不直接使用 ptr 而要多多加一个__mptr。

(2)(type *)( (char *)__mptr - offsetof(type,member) );
这行代码的作用是通过中间变量__mptr(指向某个成员的指针)减去这个成员在容器(结构体)中的偏移来得到指向容
  器(结构体)的指针。 把__mptr转换成 char *类型,因为offsetof得到的偏移量是以字节为单位。两者相减得到结构体的起始位置,再强制转      换成type类型。
参考:http://blog.chinaunix.net/uid-20543672-id-3205315.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐