struct data_strct{
int data;
struct data_struct *prev;
struct data_struct *next;
};
但链表在linux中大量地使用,很多struct都用了链表来实现,能不能提供一套通用的接口来保证这些struct实现链表的功能,
而不用为每个struct都实现一次链表的操作呢?在linux内核中是这样实现的,先定义了一个结构体
struct head_list{
struct head_list *head;
struct head_list *next;
};
并为这个结构体实现了插入链表节点,删除链表节点等操作。
然后把这个结构放到了目标的struct中,如:
struct data_strct{
int data;
struct head_list list;
};
以后就可以通过head_list的接口去访问data_struct的数据了,不再需要为data_struct去重新编写访问链表的操作。
问题是如何通过head_list得到data_struct的数据呢?主要是下面的两个宏:
#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)
其中ptr是head_list类型的指针,type是目标struct的类型,member是head_list在目标struct的名称;
((size_t) &((TYPE *)0)->MEMBER),把地址为0的内容强制转换为(TYPE *),然后得到member的地址,
显然这是member在结构体的偏移;
const typeof( ((type *)0)->member ) *__mptr = (ptr);的typeof是获取member的类型,所以__mptr是
head_list类型的指针,减去member在结构体的偏移就是head_list所在的目标的结构体的起始位置。因此根据
该地址就可以得到结构体所有数据。
具体可运行例子如下:(注意typeof是GNU C的关键字,标准C好像没,例子用gcc可正常跑)
#include <stdlib.h>
#include <stdio.h>
#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)
struct list_head{
struct list_head *pre;
struct list_head *next;
};
struct data{
struct list_head list;
int a;
int b;
};
struct data test;
struct data *test2;
int main()
{
int offset = (size_t)&(((struct data*)0)->a);
printf("offset=%d\n",offset);
offset = (size_t)&(((struct data*)0)->b);
printf("offset=%d\n",offset);
test.a = 1;
test.b = 2;
struct list_head * head = &test.list;
test2 = container_of(head,struct data,list);
printf("%d\n",test2->a);
printf("%d\n",test2->b);
return 0;
}
这让我想起了使用一个含有一个字符的char数组实现变长数组的trick:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct data{
int a;
int b;
char ptr[0];
};
struct st_else1{
int a;
};
struct st_else2{
char b[3];
};
int main()
{
struct st_else1 e1 = {1};
struct st_else2 e2 = {"11"};
struct data *dt = (struct data*)malloc(sizeof(struct data)+sizeof(struct st_else1)-1);
memcpy(dt->ptr,&e1,sizeof(struct st_else1));
printf("%d\n",*dt->ptr);
free(dt);
dt = (struct data*)malloc(sizeof(struct data)+sizeof(struct st_else2)-1);
memcpy(dt->ptr,&e2,sizeof(struct st_else2));
printf("%s\n",dt->ptr);
free(dt);
return 0;
}
char ptr[0]只能放在最后,且有且只能有一个。