container_of 宏定义
-
- [一. container_of(ptr, type, member) 宏](#一. container_of(ptr, type, member) 宏)
- [二. container_of 宏的应用](#二. container_of 宏的应用)
一. container_of(ptr, type, member) 宏
container_of
宏是嵌入式和Linux中广泛使用的一个宏,用于通过结构体成员的地址来获取其所属结构体的起始地址。
定义如下:
c
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type, member) ); })
该定义中需要传入三个参数,分别是
- ptr 结构体变量成员的指针
- type 结构体的类型
- member 结构体type中的成员名字
返回的是结构体变量的首地址。
该宏定义的工作原理:
1.类型检查
typeof( ((type *)0)->member )
用于获取成员 member
的类型。随后将 ptr
赋值给该类型的指针 __mptr
,这一步用于确保ptr指针类型匹配。
2.计算偏移量
offsetof(type, member)
计算成员 member
在结构体 type
中的偏移量。其实现通常为:
c
#define offsetof(type, member) ((size_t)&((type *)0)->member)
通过将结构体指针指向地址 0
,直接获取成员地址作为偏移量(编译器优化,无需实际解引用空指针)。
例如:
c
#include "stdio.h"
#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) ); })
struct T{
char ch;
int a;
short b;
}__attribute__ ((packed)); // 取消结构体在编译过程中的优化对齐,按照实际占用字节数进行对齐
int main(void){
struct T st = {'a',1,3};
int *p = &st.a;
printf("sizeof(st) = %d\n",sizeof(st)); // 7
printf("offsetof(struct T,a) = %d\n",offsetof(struct T,a)); // 1
printf("st addr = %x \n",&st);
printf("container_of(p,struct T, a) = %x",container_of(p,struct T, a));
return 0;
}
output:
c
sizeof(st) = 7
offsetof(struct T,a) = 1
st addr = dfdffd29
container_of(p,struct T, a) = dfdffd29
3.地址转换
将 __mptr
转换为 char*
(确保指针按字节计算),减去偏移量得到结构体的起始地址,最后转换为 type*
类型。
可以把 container_of(ptr, type, member) 展开,并简化为下面这种写法,只需要保证传入的ptr指针是属于type里面的成员。
c
#define container_of(ptr, type, member) \
((type *) ((char *) (ptr) - (size_t)(&(((type *)0)->member))))
二. container_of 宏的应用
在链表中,经常使用 container_of
来简化链表的遍历,下面给出一个演示代码:
在单链表中,以下代码来自rt-thread rt-service.h 头文件
c
/**
* @brief get the struct for this single list node
* @param node the entry point
* @param type the type of structure
* @param member the name of list in structure
*/
#define rt_slist_entry(node, type, member) \
rt_container_of(node, type, member)
/**
* rt_slist_for_each - iterate over a single list
* @pos: the rt_slist_t * to use as a loop cursor.
* @head: the head for your single list.
*/
#define rt_slist_for_each(pos, head) \
for (pos = (head)->next; pos != RT_NULL; pos = pos->next)
/**
* rt_slist_for_each_entry - iterate over single list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your single list.
* @member: the name of the list_struct within the struct.
*/
#define rt_slist_for_each_entry(pos, head, member) \
for (pos = rt_slist_entry((head)->next, typeof(*pos), member); \
&pos->member != (RT_NULL); \
pos = rt_slist_entry(pos->member.next, typeof(*pos), member))
/**
* rt_slist_first_entry - get the first element from a slist
* @ptr: the slist head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the slist_struct within the struct.
*
* Note, that slist is expected to be not empty.
*/
#define rt_slist_first_entry(ptr, type, member) \
rt_slist_entry((ptr)->next, type, member)
/**
* rt_slist_tail_entry - get the tail element from a slist
* @ptr: the slist head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the slist_struct within the struct.
*
* Note, that slist is expected to be not empty.
*/
#define rt_slist_tail_entry(ptr, type, member) \
rt_slist_entry(rt_slist_tail(ptr), type, member)
如果是循环链表,则可以改为下面:
c
/**
* @brief get the struct for this entry
* @param node the entry point
* @param type the type of structure
* @param member the name of list in structure
*/
#define rt_list_entry(node, type, member) \
rt_container_of(node, type, member)
/**
* rt_list_for_each - iterate over a list
* @pos: the rt_list_t * to use as a loop cursor.
* @head: the head for your list.
*/
#define rt_list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
/**
* rt_list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the rt_list_t * to use as a loop cursor.
* @n: another rt_list_t * to use as temporary storage
* @head: the head for your list.
*/
#define rt_list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* rt_list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define rt_list_for_each_entry(pos, head, member) \
for (pos = rt_list_entry((head)->next, typeof(*pos), member); \
&pos->member != (head); \
pos = rt_list_entry(pos->member.next, typeof(*pos), member))
/**
* rt_list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define rt_list_for_each_entry_safe(pos, n, head, member) \
for (pos = rt_list_entry((head)->next, typeof(*pos), member), \
n = rt_list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = rt_list_entry(n->member.next, typeof(*n), member))
/**
* rt_list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*
* Note, that list is expected to be not empty.
*/
#define rt_list_first_entry(ptr, type, member) \
rt_list_entry((ptr)->next, type, member)