避开container_of的坑
container_of宏简介
container_of
是Linux内核中一个常用的宏,用于根据结构体成员的地址获取整个结构体的地址。它在内核链表、设备驱动等场景中广泛使用。该宏定义在<linux/kernel.h>
中,其原型如下:
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
container_of宏的使用方法
假设有一个结构体struct foo
,其中包含一个成员int bar
。已知bar
的地址,可以通过container_of
获取struct foo
的地址。
struct foo {
int baz;
int bar;
};
int *ptr = &foo_instance.bar;
struct foo *parent = container_of(ptr, struct foo, bar);
container_of
的三个参数分别是:
ptr
:结构体成员的指针。type
:结构体的类型。member
:成员在结构体中的名称。
container_of宏的实现原理
container_of
的核心是计算成员在结构体中的偏移量,然后通过成员的地址减去偏移量得到结构体的起始地址。具体步骤如下:
- 使用
typeof
获取成员的类型,并声明一个临时指针__mptr
指向传入的成员地址,这一步主要用于类型检查。 - 使用
offsetof
宏计算成员在结构体中的偏移量。 - 将成员地址减去偏移量,得到结构体的起始地址。
container_of宏的使用技巧
技巧1:类型安全检查container_of
宏的第一行通过typeof
进行类型检查,确保传入的指针类型与结构体成员的类型匹配。如果类型不匹配,编译器会报错。
技巧2:结合链表使用container_of
常用于内核链表的实现中。例如,通过链表节点的地址获取包含该节点的结构体地址:
struct list_head {
struct list_head *next, *prev;
};
struct foo {
int data;
struct list_head list;
};
struct list_head *node = ...;
struct foo *parent = container_of(node, struct foo, list);
技巧3:避免硬编码偏移量container_of
通过offsetof
自动计算偏移量,避免了手动计算偏移量的错误风险。
container_of宏的易错点
易错点1:成员名称拼写错误
如果成员名称拼写错误,编译器不会报错,但会导致运行时计算出错误的结构体地址。例如:
struct foo *parent = container_of(ptr, struct foo, barr); // 错误:成员名应为bar
易错点2:指针类型不匹配
如果传入的指针类型与结构体成员类型不匹配,编译器可能会报出类型不匹配的警告或错误。例如:
char *ptr = (char *)&foo_instance.bar;
struct foo *parent = container_of(ptr, struct foo, bar); // 错误:ptr类型应为int *
易错点3:结构体类型错误
如果结构体类型错误,offsetof
会计算出错误的偏移量,导致访问非法内存。例如:
struct bar {
int x;
int y;
};
int *ptr = &foo_instance.bar;
struct bar *wrong_parent = container_of(ptr, struct bar, y); // 错误:类型应为struct foo
*易错点4:成员是指针
如果是结构体中的成员是指针类型,则传入的时候要该指针的地址。这个地方需要注意的是,该指针是否是结构体成员中的指针。
struct test {
int a;
int b;
int c;
};
struct test1 {
struct test *p1;
};
struct test2 {
struct test *p2;
};
例如,test1和test2中都有一个指向test的指针p1和p2,如果此时要根据p2反向查找test2,但给的参数是p1的指针,那反向得到的会是test1,因此,反向查找时候,只能传递给本成员的指针,别的成员中或许也有该指针,但不能使用!
总结
container_of
是Linux内核中一个强大的工具,能够通过成员地址高效地获取结构体地址。使用时需注意成员名称、类型和结构体类型的正确性,避免因拼写错误或类型不匹配导致的运行时错误。结合链表等数据结构使用时,container_of
能够显著简化代码并提高可维护性。
更多推荐
所有评论(0)