container_of宏到底做了什么???

本文详细解析了Linux内核中的container_of宏的工作原理,解释了如何通过已知结构体成员的指针找到结构体的首地址,涉及offsetof宏的使用及具体计算过程。

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

首先,她的作用是: 如果知道了结构体中某个元素的指针,就可以利用该宏得到指向结构体首地址的指针,这样可以用指针访问结构体的所用变量。

在Linux内核中,她的定义如下:

#define container_of(ptr, type, member)  ({            \
    const typeof(((type *)0)->member) * __mptr = (ptr);    \
    (type *)((char *)__mptr - offsetof(type, member)); })

 

参数:

   ptr 是一个已知的指针,该指针指向结构体某个成员。

   type 是要计算的结构体类型。

   member 是要结构体中的某个成员。(就是根据该成员计算出结构体的首地址。) 

 

  第一条语句:

    const typeof(((type *)0)->member) * __mptr = (ptr);  

   利用typeof关键字,将__mptr指针变量转换成member成员的数据类型,并将ptr传入的指针赋值给__mptr。

第二条语句:

   (type *)((char *)__mptr - offsetof(type, member));

  将__mptr减去由offsetof计算member在结构体中的偏移量,这也就是container_of的最终结果。那offsetof又做了什么?

她的定义如下:

       #define offsetof(TYPE, MEMBER)    ((size_t) &((TYPE *)0)->MEMBER)

将 0 转换成 TYPE 类型的指针,也就是结构体类型的指针,改指针指向了MEMBER成员,然后取其地址,因为地址是0(可以抽象的这么看是0),所以offsetof所得到的结果就是 MEMBER 成员相对于结构体首地址的偏移量,那在计算的时候,利用MEMBER该成员的地址减去MEMBER相对于结构体首地址的偏移量,就会得到结构体的首地址。

例如:

  某个结构体的首地址是 X, 而知道某个成员的地址是0x40001314,现在求 X 的地址是多少?

    也就是计算这个:   (type *)((char *)__mptr - offsetof(type, member));   

     假如由 offsetof 计算出 member 成员相对于结构体的首地址是 14 ,而又知道__mptr = 0x40001314,所以结构体的首地址是:0x40001300.

<think>我们正在讨论container_of的定义。根据引用[1]和[2],container_of的作用是通过一个结构体变量中某个成员的地址来获取整个结构体变量的首地址。它的原型定义如下: ```c #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) ``` 这个接受三个参数: - `ptr`:指向结构体成员(member)的指针。 - `type`:结构体的类型。 - `member`:结构体中的成员名称。 的实现分为两个部分: 1. 首先,使用`typeof`关键字获取成员的类型,并定义一个与成员类型相同的临时指针`__mptr`,并将其赋值为`ptr`。这一步是为了进行类型检查,确保传入的指针类型与成员类型匹配。 2. 然后,计算结构体的起始地址。这里用到了`offsetof`(用于计算成员在结构体中的偏移量)。将成员指针`__mptr`转换为`char*`类型(因为`char*`的偏移以字节为单位),然后减去该成员在结构体中的偏移量,得到结构体的起始地址。最后将结果转换为`(type*)`类型。 另外,引用[4]中提到了`offsetof`的定义: ```c #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) ``` `offsetof`通过将地址0强制转换为指向结构体`TYPE`的指针,然后取成员`MEMBER`的地址,这个地址就是该成员相对于结构体起始地址的偏移量(因为结构体起始地址为0)。 因此,`container_of`的核心就是利用`offsetof`计算偏移量,然后通过成员地址减去偏移量得到结构体首地址。 注意:在`container_of`中,第一行(定义`__mptr`)主要是为了类型安全,防止传入的指针类型与成员类型不匹配。在有些实现中,可能会省略这一行(如引用[4]中提到的),但为了安全,通常保留。 **相关问题**: 1. `container_of`中的`offsetof`是如何计算结构体成员偏移量的? 2. 为什么在`container_of`中需要将指针转换为`char*`类型? 3. 使用`container_of`时需要注意哪些问题?</think>### container_of的原型定义 `container_of`是Linux内核中常用的一个,其核心作用是通过结构体成员的地址反推整个结构体的起始地址。原型定义如下(参考自Linux内核源码): ```c #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type, member) );}) ``` #### 参数说明: 1. `ptr` - 指向**结构体成员**的指针(例如指向结构体中`int age`成员的指针) 2. `type` - 结构体的**类型名**(例如`struct person`) 3. `member` - 结构体中**成员的名字**(例如`age`) #### 关键实现解析: 1. **类型安全检查** ```c const typeof( ((type *)0)->member ) *__mptr = (ptr); ``` - 使用`typeof`获取成员的类型 - 声明临时指针`__mptr`并赋值为`ptr` - 确保传入的指针类型与成员类型匹配[^1][^4] 2. **偏移量计算** ```c (char *)__mptr - offsetof(type, member) ``` - 将成员指针转换为`char*`(确保字节级地址运算) - 减去成员在结构体中的偏移量(通过`offsetof`实现) - 最终得到结构体的起始地址[^2][^3] 3. **offsetof原理** ```c #define offsetof(type, member) ((size_t)&((type *)0)->member) ``` - 将地址`0`强制转换为`(type*)`类型 - 取成员地址`&((type*)0)->member`,此时成员地址即偏移量 - 转换为`size_t`类型返回[^3][^4] #### 使用示例: ```c struct person { char name[20]; int age; // 假设偏移量为20字节 }; struct person p; int *age_ptr = &p.age; // 通过age_ptr获取person结构体地址 struct person *p_ptr = container_of(age_ptr, struct person, age); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值