当前位置: 首页 > news >正文

【C语言】container_of 宏定义

container_of 宏定义

    • 一. container_of(ptr, type, member) 宏
    • 二. container_of 宏的应用

一. container_of(ptr, type, member) 宏

container_of 宏是嵌入式和Linux中广泛使用的一个宏,用于通过结构体成员的地址来获取其所属结构体的起始地址。

定义如下:

#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 中的偏移量。其实现通常为:

#define offsetof(type, member) ((size_t)&((type *)0)->member)

通过将结构体指针指向地址 0,直接获取成员地址作为偏移量(编译器优化,无需实际解引用空指针)。

例如:

#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:

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里面的成员。

#define container_of(ptr, type, member) \
((type *) ((char *) (ptr) - (size_t)(&(((type *)0)->member))))

二. container_of 宏的应用

在链表中,经常使用 container_of 来简化链表的遍历,下面给出一个演示代码:

在单链表中,以下代码来自rt-thread rt-service.h 头文件

/**
 * @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)

如果是循环链表,则可以改为下面:

/**
 * @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)

相关文章:

  • 数据集 handpose_x_plus 3D RGB 三维手势多场景
  • Nginx 配置文件解析
  • 内存池项目(1)——前置知识
  • CF2074F Counting Necessary Nodes
  • 电动垂直起降飞行器(eVTOL)
  • 每天五分钟深度学习框架pytorch:搭建LSTM完成时间序列的预测
  • 汉明码:从奇偶校验到错误精确定位的传奇编码
  • 【11408学习记录】英语通知写作速成攻略:框架拆解+宾语从句疑难全破解
  • 21 天 Python 计划:MySQL 库相关操作
  • DB-Mysql中TIMESTAMP与DATETIME的区别
  • 【Flask开发】嘿马文学web完整flask项目第3篇:2.用户认证,2.用户认证【附代码文档】
  • 【STM32单片机】#6 定时器比较输出
  • OceanSim: 基于Isaac Sim GPU 加速水下机器人感知仿真框架
  • 基于SpringBoot酒店管理系统设计和实现(源码+文档+部署讲解)
  • qt socket编程正确重启tcpServer的姿势
  • 同一份数据,Redis为什么要存两次
  • 人脸考勤管理一体化系统(人脸识别系统,签到打卡)
  • WinForm真入门(9)——RichTextBox控件详解
  • 基于大数据的美团外卖数据可视化分析系统
  • 【易飞】易飞批量选择品号处理方法,工作效率提升300%
  • 做网站上饶/免费推广自己的网站
  • 政府门户网站建设的必要性/站长综合查询工具
  • web网站开发视频/流量购买网站
  • 公司做网站推广/山东网站建设
  • 网络游戏公司/seo主要做什么
  • 福州开发企业网站/百度最新版下载