在用户空间程序也使用container_of()

本文详细介绍了如何在用户空间程序中自定义container_of宏,用于将成员指针转换为包含该成员的结构体指针,简化了驱动程序中结构体操作的复杂性。

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

Linux在内核空间中存在一个非常重要且实用的宏container_of(),其作用就是通过一个结构变量中一个成员的地址找到这个结构体变量的首地址。在写驱动程序时非常实用,但是此宏是定义在include/linux/kernel.h中的:

/** 
* container_of - cast a member of a structure out to the containing structure 
* @ptr:        the pointer to the member. 
* @type:       the type of the container struct this is embedded in. 
* @member:     the name of the member within the struct. 
* */ 

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

如果想在用户空间程序也使用此宏的话,可以在用户程序中包含kernel.h,但在编译时需要额外添加包含内核的编译目录。

其实我们完全可以在用户程序中自己定义此宏函数,这样便正常使用,也不需要额外操作。以下是我做的实验code:

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>    // 必须添加此项,offsetof定义在此文件中

struct my_type {
    char *name;
    int no;
    int age;
};

struct my_type nae = {
    .name = "hello c",
    .no = 12345,
    .age = 18,
};

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

int main()
{
    int *tm = &(nae.no);
    struct my_type *my = container_of(tm, struct my_type, no);

    printf("name = %s\n", my->name);
    printf("no = %d\n", my->no);
    printf("age = %d\n", my->age);

    return 0;

}
实验结果:

name = hello c
no = 12345
age = 18



<think>我们正在讨论container_of,这是一个在Linux内核中广泛使用,用于根据结构体成员的地址获取整个结构体的起始地址。 根据引用[1]和引用[2]的内容,container_of的作用是:通过一个结构体成员的指针,推算出整个结构体的首地址。 实现原理:利用结构体成员在结构体中的偏移量,将成员指针减去该偏移量即可得到结构体的起始地址。 具体实现步骤: 1. 首先,通过offsetof计算出成员在结构体中的偏移量。offsetof的定义通常是:将0地址强制转换为结构体类型指针,然后访问成员,此时成员的地址就是偏移量(因为结构体起始地址为0)。 2. 然后,将成员指针(ptr)减去它在结构体中的偏移量,就得到了结构体的起始地址。 注意:在container_of的实现中,有一个类型检查的步骤,即使用typeof来获取成员的类型,并声明一个中间指针变量mptr来确保ptr的类型正确。 下面我们具体解释: 定义: #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type, member) );}) 步骤: a. 第一行:声明一个与成员类型相同的指针__mptr,并将ptr赋值给它。这一步是为了进行类型检查(如果ptr的类型与成员类型不一致,编译器会警告)。 b. 第二行:将__mptr转换为char*(因为char*的加减是以字节为单位的),然后减去该成员在结构体中的偏移量(通过offsetof得到),最后将结果转换为(type *)类型。 其中,offsetof的定义: #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 解释offsetof:将0地址强制转换为TYPE类型的指针,然后取成员MEMBER的地址,这个地址就是MEMBER相对于结构体起始地址的偏移量(因为结构体起始地址为0)。 示例: 假设我们有一个结构体: struct example { int a; char b; double c; }; 如果我们有一个指向成员b的指针ptr_b,那么我们可以通过container_of(ptr_b, struct example, b)来得到整个结构体的起始地址。 手动计算过程: 1. 计算b在struct example中的偏移量:因为a是int(假设4字节),b是char(1字节),所以b的偏移量是4(假设不考虑对齐,实际要考虑对齐,但这里简化)。 2. 将ptr_b减去4,就得到了结构体的起始地址。 注意:在实际编程中,我们通常用在链表操作中,比如通过链表节点(作为结构体成员)的指针来获取包含该节点的整个结构体。 下面我们用一个简单的程序来演示container_of使用(注意:在内核中可以直接使用,在用户空间需要自己定义offsetofcontainer_of):</think>### `container_of`的作用与原理 #### 1. **核心作用** `container_of`用于**通过结构体成员的地址反推整个结构体的起始地址**。它在内核开发中广泛应用,尤其在链表、树等数据结构中,允许通过节点成员的指针直接访问其所属的完整结构体实例[^1][^2]。 #### 2. **实现原理** 其核心是计算**成员在结构体中的偏移量**,再通过指针运算反推结构体首地址。实现分为两步: ##### (1) 计算成员偏移量(`offsetof`) ```c #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) ``` - **原理**:将地址`0`强制转换为`TYPE*`类型,此时成员`MEMBER`的地址即为其在结构体中的偏移量(因为结构体首地址为`0`)。 - **示例**: 若结构体`struct S { int a; char b; }`,则 `offsetof(struct S, b) = 4`(假设`int`占4字节)。 ##### (2) 反推结构体首地址(`container_of`) ```c #define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) *__mptr = (ptr); \ // 类型安全检查 (type *)((char *)__mptr - offsetof(type, member)); \ // 偏移量逆向计算 }) ``` - **参数说明**: - `ptr`:成员的实际地址 - `type`:结构体类型 - `member`:成员在结构体中的名称 - **计算步骤**: 1. 通过`offsetof`获取成员偏移量; 2. 将成员指针`ptr`转换为`char*`(确保按字节计算); 3. 减去偏移量得到结构体首地址,并转换回`type*`类型。 #### 3. **应用示例** ```c struct Person { int age; char name[20]; }; // 已知name的地址,反推Person结构体地址 char *name_ptr = &person_instance.name; struct Person *p = container_of(name_ptr, struct Person, name); // 此时 p 指向 person_instance 的起始地址 ``` 此操作在Linux内核链表中常见,例如通过`list_head`节点访问其所属的结构体。 --- ### 相关问题 1. **如何手动实现`offsetof`?其原理是否依赖具体硬件架构?** 2. **使用`container_of`时,若传入的成员指针类型错误会导致什么问题?** 3. **在C++中能否直接使用`container_of`?需要如何修改?** [^1]: container_of 是一个函数,它被称为内核第一。作用:根据一个结构体成员的名称和地址以及结构体的类型,可以计算出结构体变量的地址。 [^^2]: container_of 用于通过一个结构体成员的指针,推算出整个结构体的首地址。在内核开发中,通常通过链表节点等成员来操作整个结构体。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值