计算结构体成员相对于结构体的偏移
#include <stdio.h>
// #include <stddef.h>
#define offsetof(type, member) (size_t) &((type*)0)->member
typedef struct {
char a;
int b;
int c;
} test;
int main(void) {
printf("offset: %ld %ld\r\n", offsetof(test, b), offsetof(test, c));
return 0;
}
offset: 4 8
此宏的实现为
#define offsetof(type, member) (size_t) &((type*)0)->member
通过结构体成员地址找到结构体对象首地址
#include <stdio.h>
#define offsetof(type, member) (size_t) & ((type *)0)->member
#define container_of(ptr, type, member) \
({ \
const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); \
})
typedef struct {
char a;
int b;
float c;
} test_t;
int main(void) {
test_t test, *p_test;
int *container_test = &(test.b);
p_test = container_of(container_test, test_t, b);
if (&test == p_test) {
printf("Addr is samed\r\n");
}
}
Addr is samed
整个宏展开为 ({})
, 这是一个 GNU 扩展的 C 语法,称为 braced-group in expression.编译器将计算整个块,并使用块中包含的最后一个语句的值, 例如如下代码将打印 5
int x = ({1; 2;}) + 3;
printf("%d\n", x); // 5
typeof 也是一个 GNU C 扩展, 它接受一个参数并返回其类型
第一行对于宏的结果本质上并不重要,但它用于类型检查目的.第二行则是从结构体的地址中减去结构成员的偏移量,得到容器结构的地址
一个比较详细的解释见: understand container_of macro
就宏的作用而言,确实只需要最后的 (type )((char)ptr - offsetof(type,member) )就可以得到正确结果.但是以上都是假设输入都是正确的,对于不正确的输入,我们的宏应当有相应的检查与报错机制.这正是 const typeof( ((type )0)->member ) __mptr = (ptr); 的意义所在.它可以检查 ptr的类型是否正确.以下面代码为例
#define container_of
struct A { int i; float j; };
struct A k;
float *i = &A.j;
struct A *ptr = container_of(i, struct A, i);
warning: initialization of 'const int *' from incompatible pointer type 'float *'
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
static_assert(__same_type(*(ptr), ((type *)0)->member) || \
__same_type(*(ptr), void), \
"pointer type mismatch in container_of()"); \
((type *)(__mptr - offsetof(type, member))); })
其中 static_cast就是对 C11 中 _Static_assert的简单 wrapper,这也是内核标准切换到 C11 后获得的好处之一.甚至还有一个更加 safe 的 container_of_safe宏.当 ptr是空指针或者指针值不是合法值时不转换.
#define container_of_safe(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
static_assert(__same_type(*(ptr), ((type *)0)->member) || \
__same_type(*(ptr), void), \
"pointer type mismatch in container_of_safe()"); \
IS_ERR_OR_NULL(__mptr) ? ERR_CAST(__mptr) : \
((type *)(__mptr - offsetof(type, member))); })