1. 问题描述:api
如今定义了一个结构体:数据结构
struct Foo函数
{spa
int a;指针
int b;code
};blog
Foo foo;内存
假如因为函数传参等缘由,如今程序只能拿到 foo.b 的地址,这时想经过某种方法获取到 foo 结构体里的其余成员。io
那么问题来了,这就是如下主要讨论的内容。class
2. 原理概述
将地址 0 强制转换成一个结构体指针,伪代码: struct foo *p = (struct Foo *)0;
从而经过已知的结构体成员的地址减去结构体的首地址获得已知结构体成员的内存偏移 , 伪代码 : offset = &(p->b) - p;
那么问题就简单了, 如今已知 foo.b 的地址,将其减去偏移便可获得该结构体的首地址。
3. 实现举例
//file name : list_head.c #include <stdio.h> struct list_head { struct list_head *next; }; struct fox { unsigned int tail_color; unsigned int tail_length; struct list_head list; }; #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER ) #define container_of(ptr, type, member) ({\ const typeof( ((type *)0)->member)* __mptr = (ptr);\ (type *)((char*)__mptr - offsetof(type, member));}) int main(int argc, char** argv) { unsigned short offset = 0; struct fox *p = 0; struct fox red_fox; red_fox.tail_color = 45; red_fox.tail_length = 10; unsigned int *p_t; printf("red_fox_addr: %x\n", &red_fox); printf("tail_color_addr: %x\n", &(red_fox.tail_color)); printf("tail_length_addr: %x\n", &(red_fox.tail_length)); printf("list_addr: %x\n", &(red_fox.list)); // offset = (unsigned char*)&(p->list) - (unsigned char*)p; offset = offsetof(struct fox, list); printf("offset: %d \n", offset); p_t = container_of(&red_fox.list, struct fox, list); printf("red_fox_addr: %x\n", p_t); printf("tail_color: %d \n", ((struct fox *)p_t)->tail_color); printf("tail_length: %d \n", ((struct fox *)p_t)->tail_length); return 0; }
4. 应用
Linux 中数据结构单链表使用的这种方法。好处也是显而易见的,当用户想经过单链表实现本身封装的数据结构时不须要在单独结构体定义单链表遍历的指针和相关函数,仅仅实现包含 list_head 这个结构体成员便可。而内核提供了完整且高效的用于单链表操做 api.
做者能力有限,如发现错误欢迎指正。