在嵌入式开发和操作系统内核编程中,container_of 宏是一个非常重要的工具。它允许开发者通过结构体成员的地址反推出整个结构体的地址,从而实现对结构体的访问。这种能力在面向对象编程中尤为重要,特别是在 C 语言中,由于缺乏类的概念,container_of 宏弥补了这一缺陷。本文将详细介绍 container_of 宏的定义、工作原理以及其在实际开发中的应用场景。
什么是 container_of 宏
container_of 宏是一种宏定义,通常用于 C 语言中。它的作用是通过结构体中的某个成员变量的地址,推导出该成员所属的整个结构体的地址。这在链表、树等数据结构的操作中尤为重要。
container_of 宏的作用
container_of 宏的主要作用是:
结构体成员定位:通过成员变量的地址,找到包含该成员的整个结构体。
类型安全:确保类型匹配,避免类型错误导致的运行时问题。
标准定义
在 Linux 内核中,container_of 宏的标准定义如下:
#define container_of(ptr, type, member) \
({ const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type, member) ); })
各部分解析
typeof 关键字
typeof 是 GCC 提供的关键字,用于获取表达式的类型。在这里,它用于获取 member 成员的类型。
offsetof 宏
offsetof 是标准 C 库中的宏,用于计算结构体成员相对于结构体起始地址的偏移量(以字节为单位)。
类型转换
宏通过减去偏移量的方式,将成员变量的地址转换为整个结构体的地址。
偏移量计算
假设有一个结构体 struct example,其中包含一个成员变量 int data:
struct example {
int data;
};
如果知道 data 的地址,可以通过 offsetof 宏计算其相对于结构体起始地址的偏移量:
size_t offset = offsetof(struct example, data);
地址转换
假设 ptr 是 data 成员的地址,通过以下公式可以计算出整个结构体的地址:
(char *)ptr - offset
最终的结果需要强制转换为结构体指针类型:
(struct example *)((char *)ptr - offset)
示例代码
以下是一个完整的示例代码,展示 container_of 宏的工作原理:
#include <stdio.h>
#include <stddef.h>
struct example {
int data;
};
void print_container(void *ptr) {
struct example *container = container_of(ptr, struct example, data);
printf("Container address: %p\n", (void *)container);
}
int main() {
struct example obj = { .data = 42 };
int *data_ptr = &obj.data;
print_container(data_ptr);
return 0;
}
输出结果:
Container address: 0x7ffeeb8b9a10
链表操作
在链表中,节点通常包含指向下一个节点的指针,以及一些其他信息。通过 container_of 宏,可以从节点的指针反推出整个节点所在的结构体。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
struct node {
int value;
struct node *next;
};
#define container_of(ptr, type, member) \
({ const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type, member) ); })
void print_list(struct node *head) {
struct node *current = head;
while (current != NULL) {
printf("%d -> ", current->value);
current = container_of(current->next, struct node, next);
}
printf("NULL\n");
}
int main() {
struct node n1 = { .value = 1, .next = NULL };
struct node n2 = { .value = 2, .next = NULL };
n1.next = &n2;
print_list(&n1);
return 0;
}
输出结果:
1 -> 2 -> NULL
设备驱动程序
在设备驱动程序中,通常需要将设备结构体与设备文件关联起来。通过 container_of 宏,可以从文件描述符反推出设备结构体。
示例代码:
#include <stdio.h>
#include <stddef.h>
struct device {
int id;
char name[32];
};
#define container_of(ptr, type, member) \
({ const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type, member) ); })
void print_device(void *dev_ptr) {
struct device *dev = container_of(dev_ptr, struct device, name);
printf("Device ID: %d, Name: %s\n", dev->id, dev->name);
}
int main() {
struct device d = { .id = 1, .name = "USB Device" };
print_device(d.name);
return 0;
}
输出结果:
Device ID: 1, Name: USB Device
内核编程
在 Linux 内核中,container_of 宏被广泛用于实现链表、哈希表等数据结构。例如,在中断处理程序中,可以通过 container_of 宏从中断描述符反推出对应的设备结构体。
类型安全性
container_of 宏通过 typeof 和 offsetof 宏确保了类型的安全性,避免了手动计算偏移量可能导致的错误。
可移植性
container_of 宏基于标准 C 库的 offsetof 宏,因此具有良好的可移植性,可以在不同的编译器和平台上使用。
简化代码
通过 container_of 宏,开发者无需手动计算偏移量,简化了代码编写过程,提高了代码的可读性和维护性。
container_of 宏是 C 语言中一个非常实用的工具,尤其在嵌入式开发和操作系统内核编程中发挥了重要作用。通过结构体成员的地址,它可以反推出整个结构体的地址,从而实现对结构体的访问。本文详细介绍了 container_of 宏的定义、工作原理以及其在链表操作、设备驱动程序和内核编程中的应用场景。希望本文能帮助开发者更好地理解和使用 container_of 宏,从而提高代码的质量和效率。
声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com
通过车辆vin码查询车辆的过户次数等相关信息
验证银行卡、身份证、姓名、手机号是否一致并返回账户类型
查询个人是否存在高风险行为
支持全球约2.4万个城市地区天气查询,如:天气实况、逐日天气预报、24小时历史天气等
支持识别各类商场、超市及药店的购物小票,包括店名、单号、总金额、消费时间、明细商品名称、单价、数量、金额等信息,可用于商品售卖信息统计、购物中心用户积分兑换及企业内部报销等场景