c语言函数-containerof()

今天在看ESP32库的源码时,发现了一个神奇的函数:__containerof()。可以从结构体成员的地址获得该结构体的地址,实现一种类似于找father的方法,故记录。

函数原型

1
2
3
#define container_of(ptr, type, member) ({              \         
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
1
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

函数作用

函数实现的功能也很简单,用来已知成员的地址和名称以及该结构体的类型,求该结构体的地址

分析

该函数的核心步骤是:

1
type的起始地址 = ptr - size      (这里需要都转换为char *,因为它为单位字节)

其中的核心是size的获得。

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

这里使用了0地址的用法。当一个0地址被强转为TYPE结构体的地址,那么它成员的地址就会变成该成员相对于结构体地址的偏移。(TYPE *)0)->MEMBER指向了成员,(size_t) &((TYPE *)0)->MEMBER取了这个成员的地址并强转为size_t

然后是这一看起来没有用的一行。

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

这一行是用来提供警告的,如果传入的参数参数类型不一样(可能是传错了),这就会产生一个警告,帮助排查错误。

最后,有一个宏函数的返回问题。

1
2
3
4
5
6
7
8
9
#define kaddr(addr)\
({\
int tmp = addr;\
if (addr > 5) \
tmp = 2;\
else\
tmp = 3;\
(addr+tmp);\
})\

上述函数在最后一句不是一个完整的句子,而只有右值。该右值就会作为返回值返回。