C的offsetof和container_of

本文介绍了一个简单的C语言程序,该程序演示了如何使用offsetof宏来计算结构体成员相对于结构体起始位置的偏移量。通过一个具体的例子,即计算struct A中成员b的偏移量,展示了offsetof宏的基本用法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <stdio.h>

#include <stddef.h>

// #define offsetof(s, m)   (size_t)&(((s *)0)->m)

 

struct A

{

        int a;

        int b;

};

 

int main()

{

        printf("%d\n", offsetof(struct A, b));

        return 0;

}

http://blog.youkuaiyun.com/pengzhixi/article/details/4275887

 

#define container_of(ptr, type, member) \

  ((type *) ((char *) (ptr) - offsetof(type, member)))

 

<think>好的,我现在需要详细解释C语言中的container_of宏的作用使用方法。首先,我得回忆一下这个宏的基本概念。根据用户提供的引用,container_of宏的作用是通过结构体中某个成员的地址来计算出整个结构体的起始地址。这在Linux内核开发中非常常见,用于通过成员指针找到其所属的结构体实例。接下来,我需要拆解这个宏的定义。用户给出的宏定义有两个版本,一个是包含类型检查的完整版,另一个是简化版。例如,引用[1]引用[2]中的宏使用了typeofoffsetof,而引用[3]提到简化版本去掉了类型检查。需要解释每个部分的作用,比如typeof用于获取成员的类型,offsetof计算成员在结构体中的偏移量。然后,应该举例说明如何使用这个宏。假设有一个结构体structexample,其中包含一个int成员value。如果有一个指向value的指针,如何用container_of找到对应的struct example实例的地址。这里需要具体代码示例,并逐步解析宏展开的过程,尤其是如何通过指针运算减去偏移量得到结构体地址。 另外,需要强调使用场景。比如在内核链表中,链表节点作为结构体的成员,通过container_of可以方便地从链表节点获取包含它的整个结构体,这在遍历链表时非常有用。引用[2]引用[3]提到了移植兼容性问题,需要提到GNUC扩展的使用以及可能存在的编译器兼容性问题。还要注意潜在的问题注意事项。例如,如果传入的指针不是结构体的有效成员地址,结果将不可预测。类型检查的部分虽然增加了安全性,但依赖于特定编译器的支持,这可能在某些环境下不可用,如引用[3]所述。 最后,生成相关问题时要确保覆盖可能的相关知识点,比如offsetof宏的实现、类型检查的作用、以及在不同环境下的替代方法等。需要确保回答结构清晰,逐步引导用户理解,并保持语言的中文口语化,避免过于技术化的术语,同时正确使用LaTeX格式引用标记。</think>### C语言container_of宏的作用及使用方法 #### 一、核心作用 `container_of` 宏用于 **通过结构体成员的地址反向推导出所属结构体的起始地址**。其核心公式为: $$ \text{结构体地址} = \text{成员地址} - \text{成员在结构体中的偏移量} $$ 这一机制在Linux内核开发中广泛应用,例如链表操作中通过链表节点定位父结构[^1][^2]。 --- #### 二、宏定义解析 ##### 完整版定义(包含类型检查) ```c #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) );}) ``` ##### 关键组件解析 1. **`offsetof`宏**: 计算成员在结构体中的偏移量,通过将结构体指针强制转换为0地址后取成员地址实现。例如: ```c struct Student { int id; char name[20]; }; size_t offset = offsetof(struct Student, name); // 计算结果为4(假设int占4字节) ``` 2. **`typeof`运算符**: 用于获取成员的类型信息,增强类型安全性[^3]。例如: ```c typeof(struct Student->name) // 得到char[20]类型 ``` 3. **指针运算**: 将成员指针转换为`char*`类型后减去偏移量,得到结构体起始地址。 --- #### 三、使用场景示例 ##### 场景:链表节点定位父结构 ```c // 链表节点结构 struct list_head { struct list_head *prev, *next; }; // 包含链表节点的用户结构 struct Task { int priority; struct list_head node; // 嵌入的链表节点 }; // 通过链表节点找到Task结构体 struct list_head *pos = /* 某个链表节点指针 */; struct Task *task = container_of(pos, struct Task, node); ``` ##### 执行过程解析 1. 假设`node`在`Task`中的偏移量为4字节(`int priority`占4字节) 2. 若`pos`地址为`0x1000`,则: $$ \text{Task地址} = 0x1000 - 4 = 0xFFC $$ --- #### 四、数学验证 设结构体类型为$T$,其成员$m$的偏移量为$\Delta$,成员地址为$p_m$,则结构体地址$p_T$满足: $$ p_T = p_m - \Delta $$ 其中: $$ \Delta = \text{offsetof}(T, m) = \text{成员}m\text{在}T\text{中的内存偏移量} $$ --- #### 五、注意事项 1. **编译器依赖**: 完整版依赖GNU C扩展(`typeof`语句表达式`({})`),简化版可移植但失去类型检查[^3]: ```c // 简化版定义(无类型检查) #define container_of(ptr, type, member) \ ((type *)((char *)(ptr) - offsetof(type, member))) ``` 2. **错误风险**: 若`ptr`不是`type`结构体中`member`的有效指针,计算结果将指向非法内存区域。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值