container_of 宏、offsetof 宏 分析
2016-05-02 14:02
633 查看
container_of 是Linux中常用的宏,其作用就是根据结构体成员变量的地址获取结构体的地址。
container_of 在include/linux/kernel.h 中定义:
这么一个宏定义,可以说是把C语言的指针运用的出神入化。这个宏可以分三步来解读。
第一步:定义了一个与 ptr 相同类型的指针 __mptr,这个指针类型通过 typeof(((type *)0)->member) 来获得,然后将__mptr 赋值为 ptr。
typeof关键字是C语言中的一个新扩展,这个特性在linux内核中应用非常广泛,其作用就是通过一个变量获取它的类型。
要获取结构体中member成员的类型,首先将零地址强制转换为结构体类型指针( (type *)0 ),再通过这种指针得到这个结构体的 member 成员变量((type*)->member),然后通过这个变量得到它的类型(typeof(((type *)0)->member) )。
第二部:用(char *)__mptr减去member在结构体中的偏移量,得到的值就是整个结构体变量的首地址。
member成员在结构体中的偏移地址通过 offsetof 宏得到:
这个宏首先将零地址强制转换为结构体类型指针((type )0),再用这个指针得到MEMBER成员(((type *0)->MEMBER)),然后获取这个成员的地址(&(&((TYPE )0)->MEMBER)),最后将这个地址强转为size_t类型。因为这时候结构体首地址为0,所以成员变量的地址就是在结构体中断地址偏移。
第三步:用大括号将两条语句括起来,作为一个整体的语句块,在这个语句块外面在包一层小括号,使这个语句块的值可以被外部使用。
通过分析这个宏我才发现,一个语句块也是有返回值的,还能被外部使用,例如以下语句也是可以的:
总感觉为了实现container_of这个宏真是大费周章啊,用的着那么麻烦吗?自己写的话一行代码搞定:
乍一看似乎没毛病,仔细研究还是没毛病,但Linux内核中的定义方法自然有它的道理。当传入的指针ptr类型和结构中member成员类型不一样时,内核中的定义方法编译时会有一个指针类型不匹配的警告,而自己的定义方法悄无声息的编译通过了。不得不说,Linux开发人员考虑的真周到。
流云非晚
container_of 在include/linux/kernel.h 中定义:
/** * 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)); })
这么一个宏定义,可以说是把C语言的指针运用的出神入化。这个宏可以分三步来解读。
第一步:定义了一个与 ptr 相同类型的指针 __mptr,这个指针类型通过 typeof(((type *)0)->member) 来获得,然后将__mptr 赋值为 ptr。
typeof关键字是C语言中的一个新扩展,这个特性在linux内核中应用非常广泛,其作用就是通过一个变量获取它的类型。
要获取结构体中member成员的类型,首先将零地址强制转换为结构体类型指针( (type *)0 ),再通过这种指针得到这个结构体的 member 成员变量((type*)->member),然后通过这个变量得到它的类型(typeof(((type *)0)->member) )。
第二部:用(char *)__mptr减去member在结构体中的偏移量,得到的值就是整个结构体变量的首地址。
member成员在结构体中的偏移地址通过 offsetof 宏得到:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
这个宏首先将零地址强制转换为结构体类型指针((type )0),再用这个指针得到MEMBER成员(((type *0)->MEMBER)),然后获取这个成员的地址(&(&((TYPE )0)->MEMBER)),最后将这个地址强转为size_t类型。因为这时候结构体首地址为0,所以成员变量的地址就是在结构体中断地址偏移。
第三步:用大括号将两条语句括起来,作为一个整体的语句块,在这个语句块外面在包一层小括号,使这个语句块的值可以被外部使用。
通过分析这个宏我才发现,一个语句块也是有返回值的,还能被外部使用,例如以下语句也是可以的:
int a = ({ int b = 2; int c = 3; b + c;});
总感觉为了实现container_of这个宏真是大费周章啊,用的着那么麻烦吗?自己写的话一行代码搞定:
#define container_of(ptr, type, member) (type *)((char *)ptr - offsetof(type, member))
乍一看似乎没毛病,仔细研究还是没毛病,但Linux内核中的定义方法自然有它的道理。当传入的指针ptr类型和结构中member成员类型不一样时,内核中的定义方法编译时会有一个指针类型不匹配的警告,而自己的定义方法悄无声息的编译通过了。不得不说,Linux开发人员考虑的真周到。
流云非晚
相关文章推荐
- Flex AIR ANT 脚本编译
- CodeForces 667A Pouring Rain
- CodeForces 667A Pouring Rain
- hdu2925 Musical Chairs && poj3517 And Then There Was One(约瑟夫环)
- Raid 级别
- Hibernate运行报错Unknown entity: domain.UserBean
- HDU 3394 Railway 点双连通分量 + 桥
- Rails,ACM/ICPC CERC 1997,UVa514
- Insufficient memory<failed to allocate 232852> in cv::OutOfMemoryError
- LeetCode 217. Contains Duplicate
- codeforces 667A A. Pouring Rain(水题)
- poj2010 Moo University - Financial Aid(最大化第k大)
- leetcode 070 climbing stairs
- Coremail手机版页面持久型XSS实践
- kali无线渗透之Aireplay-ng小谈
- launch failed.Binary not found
- hdu 1021 Fibonacci Again
- CSU oj 1685 Entertainment Box
- 软件设计模式——责任链模式(Chain Of Responsibility)
- foxmail wine