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
/**
* 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
相关文章推荐
- Shortcuts Now Are Paid Back with Interest Later
- uva 11374 Airport Express 机场快线 迪杰斯特拉算法
- 根文件系统 编辑 http://baike.baidu.com/link?url=LzxNeeT7z7WnA6NCLzWSMHm_Z_8U-tcQouhTFCEk2UyyXloxHwMdNYAR87
- CRM_ORDER_MAINTAIN 修改订单简单示例
- CRM_ORDER_MAINTAIN 创建订单简单示例
- epoll源码实现分析[整理] http://blog.csdn.net/fengwen168168/article/details/48103009
- Epoll实现原理解析 http://blog.csdn.net/wangxiaoqin00007/article/details/14450021
- epoll_create函数实现源码分析 http://blog.csdn.net/lmh12506/article/details/7556188
- poll&&epoll实现分析(二)——epoll实现 http://blog.csdn.net/fengwen168168/article/details/48091599
- poll&&epoll实现分析(一)—poll实现 http://blog.csdn.net/fengwen168168/article/details/48091793
- 根文件系统的构建与分析(一)之流程分析 http://blog.csdn.net/jianchi88/article/details/7682901
- Linux--根文件系统的挂载过程分析 http://blog.csdn.net/guopeixin/article/details/5962482
- [LeetCode#263]Factorial Trailing Zeroes
- [LeetCode#204]Factorial Trailing Zeroes
- xml解析及编译汇总 valgrind检测内存泄露 http://blog.csdn.net/lifan5/article/details/8030285
- Drainage Ditches 最大流入门练习题,各种算法
- ld: library not found for -lAFNetworking clang: error: linker command failed with exit code 1 (use -
- [LeetCode#172]Factorial Trailing Zeroes
- 【转】使用AIDL实现进程间的通信之复杂类型传递
- 错误 1 error LNK2019: 无法解析的外部符号 _WinMain@16,该符号在函数 ___tmainCRTStartup 中被引用