# C语言宏函数container_of()怎么使用 ## 引言 在Linux内核开发中,`container_of()`宏是一个极其重要且常用的工具。它能够通过结构体成员的地址反向推导出整个结构体的起始地址,这种技术在链表、设备驱动等场景中广泛应用。本文将深入解析`container_of()`的原理、使用方法以及实际应用案例。 --- ## 一、container_of()宏概述 ### 1.1 什么是container_of() `container_of()`是Linux内核中定义的一个宏,其作用是通过结构体中某个成员的地址,计算出该结构体的起始地址。这种技术在内核数据结构(如链表、设备驱动模型)中被频繁使用。 ### 1.2 宏定义源码 ```c // Linux内核中的定义(简化版) #define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) *__mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)); })
offsetof(type, member)
是一个标准库宏,用于计算结构体成员相对于结构体起始地址的偏移量。例如:
struct sample { int a; char b; double c; }; // 假设double在64位系统偏移量为8 size_t offset = offsetof(struct sample, c); // 可能返回8
以container_of(&obj->member, struct obj, member)
为例: 1. 通过typeof
获取成员的类型并做类型检查 2. 使用offsetof
计算成员偏移量 3. 将成员指针转换为char*
(确保字节级计算) 4. 减去偏移量得到结构体起始地址
内核链表list_head
的典型用法:
struct task_struct { int pid; struct list_head tasks; // 嵌入的链表节点 }; // 通过tasks节点获取task_struct struct task_struct *task = container_of(ptr, struct task_struct, tasks);
#include <stdio.h> #include <stddef.h> #define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) *__mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)); }) struct student { int id; char name[20]; float score; }; int main() { struct student stu = {101, "Alice", 95.5}; float *score_ptr = &stu.score; // 通过score成员反推student结构体 struct student *s = container_of(score_ptr, struct student, score); printf("ID: %d, Name: %s\n", s->id, s->name); return 0; }
输出结果:
ID: 101, Name: Alice
typeof
和指针强制转换确保了类型安全。如果错误地传递了成员名,编译时会报错。
原始实现依赖GCC扩展语法({...})
,在其他编译器中可能需要改写:
// 替代方案 #define container_of(ptr, type, member) \ ((type *)((char *)(ptr) - offsetof(type, member)))
container_of()
在运行时只有简单的指针运算,没有性能开销。
对于多层嵌套的结构体,可以链式调用:
struct A { struct B b; }; struct B { int val; }; struct B *b_ptr = &obj.a.b; struct A *a_ptr = container_of(b_ptr, struct A, b);
#define get_container(ptr, member) \ container_of(ptr, typeof(*ptr), member)
C++中可以通过:
reinterpret_cast<T*>(reinterpret_cast<char*>(ptr) - offsetof(T, member))
实现类似功能,但缺乏编译时类型检查。
这种技术实现了类似”子类通过基类指针向上转型”的效果。
在kernel/sched/core.c
中,通过task_struct
的se
成员获取任务指针:
struct task_struct *p = container_of(se, struct task_struct, se);
struct device
嵌入到具体设备结构中时,常用此宏反向引用。
container_of()
宏展示了C语言指针操作的强大能力,其核心思想可以归纳为: 1. 通过成员地址减去偏移量得到基地址 2. 依赖编译器的类型检查保证安全 3. 在资源受限环境下实现高效的对象定位
掌握这个宏对于理解Linux内核和开发高性能C程序至关重要。
#ifndef container_of #define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) *__mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)); }) #endif
注意:实际内核代码还包含额外的编译器属性检查,本文为简化说明进行了适当裁剪。 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。